匯出 (0) 列印
全部展開
本文章是由人工翻譯。 將指標移到文章內的文字上方即可查看原文。 其他資訊。
譯文
原文

以非同步的方式呼叫同步方法

.NET Framework 讓您可以非同步呼叫任何方法。 若要做到這點,請使用與所要呼叫方法相同的簽章 (Signature),來定義一個委派。Common Language Runtime 會自動使用適當的簽章來定義此委派的 BeginInvokeEndInvoke 方法。

注意事項 注意事項

.NET Compact Framework 中不支援非同步委派呼叫,特別是 BeginInvokeEndInvoke 方法。

BeginInvoke 方法會啟始非同步呼叫。 此方法和您要非同步執行的方法具有相同的參數,再加上兩個額外的選擇性參數。 第一個參數是一個 AsyncCallback 委派,會參考在完成非同步呼叫時,所需要呼叫的方法。 第二個參數是將資訊傳入回呼方法的使用者定義物件。 BeginInvoke 會立即傳回,而且不會等候非同步呼叫完成。 BeginInvoke 會傳回用來監視非同步呼叫進度的 IAsyncResult

EndInvoke 方法則會擷取非同步呼叫的結果。 此方法可在 BeginInvoke 之後隨時呼叫。 如果非同步呼叫尚未完成,EndInvoke 就會封鎖呼叫執行緒,直到完成為止。 EndInvoke 的參數包括您要非同步執行之方法的 outref 參數 ( <Out> ByRefByRef 在 Visual Basic 中),加上 IAsyncResultBeginInvoke回傳。

注意事項 注意事項

Visual Studio 2005 中的 IntelliSense 功能會顯示 BeginInvokeEndInvoke 的參數。 如果不是使用 Visual Studio 或相似的工具,或者如果是以 Visual Studio 2005 使用 C#,請參閱非同步程式設計模型 (APM)中,為這些方法定義的參數描述。

此主題中的程式碼會示範使用 BeginInvokeEndInvoke 進行非同步呼叫的四種常見方法。 在呼叫 BeginInvoke 之後,您可以進行下列步驟:

  • 進行工作,然後呼叫 EndInvoke 以封鎖直到呼叫完成為止。

  • 使用 WaitHandle 中的 IAsyncResult.AsyncWaitHandle 屬性取得 WaitOne 方法來封鎖執行直到 WaitHandle信號收到為止, 然後再呼叫 EndInvoke.

  • 輪詢 BeginInvoke 所傳回的 IAsyncResult,以判斷非同步呼叫完成的時間,然後呼叫 EndInvoke

  • 為回呼方法傳遞委派到 BeginInvoke 當非同步呼叫完成時,方法就會在 ThreadPool 執行緒上執行。 回呼方法會呼叫 EndInvoke

重要事項 重要事項

無論使用哪種方式,請永遠呼叫 EndInvoke 來完成您的非同步呼叫。

下列程式碼範例會示範以非同步方式,呼叫相同長時間執行方法 TestMethod 的各種方式。 TestMethod 方法會顯示主控台訊息,表示它已經開始進行處理、等待幾分鐘,然後再結束。 TestMethodout 參數,可以示範這類參數加入到 BeginInvokeEndInvoke 之簽章的方式。 您也可以用類似的方法來處理 ref

下列程式碼範例會示範定義 TestMethod,以及可用來非同步呼叫 TestMethod、名為 AsyncMethodCaller 的委派。 若要編譯這些程式碼範例,您必須包含 TestMethodAsyncMethodCaller 委派的定義。


using System;
using System.Threading; 

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncDemo 
    {
        // The method to be executed asynchronously.
        public string TestMethod(int callDuration, out int threadId) 
        {
            Console.WriteLine("Test method begins.");
            Thread.Sleep(callDuration);
            threadId = Thread.CurrentThread.ManagedThreadId;
            return String.Format("My call time was {0}.", callDuration.ToString());
        }
    }
    // The delegate must have the same signature as the method
    // it will call asynchronously.
    public delegate string AsyncMethodCaller(int callDuration, out int threadId);
}


非同步地執行方法的最簡單方式,就是呼叫委派的 BeginInvoke 方法以啟動方法的執行,在主執行緒上進行一些工作,然後呼叫委派的 EndInvoke 方法。 EndInvoke 可能會因為呼叫執行緒尚未傳回而封鎖呼叫執行緒,直到非同步呼叫完成為止。 這是一項能在檔案或網路作業使用的良好技巧。

重要事項 重要事項

由於 EndInvoke 可能會封鎖,所以您不應該從服務使用者介面的執行緒呼叫此方法。


using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        public static void Main() 
        {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.",
                Thread.CurrentThread.ManagedThreadId);

            // Call EndInvoke to wait for the asynchronous call to complete,
            // and to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

Main thread 1 does some work.
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
 */


只要使用由 BeginInvoke 傳回之 IAsyncResultAsyncWaitHandle 屬性,您就可以取得 WaitHandle WaitHandle 會在非同步呼叫完成時收到信號,您可以呼叫 WaitOne 方法以等候此處理常式。

如果使用 WaitHandle,您就能在非同步呼叫完成的前後執行額外的處理,不過這些處理必須在呼叫 EndInvoke 以擷取結果之前進行。

注意事項 注意事項

當您呼叫 EndInvoke 時,等候控制代碼不會自動關閉。 如果您釋放所有對等候控制代碼的參考,當記憶體回收將等候控制代碼回收時,會釋放系統資源。 若要在使用完等候控制代碼時即釋放系統資源,請呼叫 WaitHandle.Close 方法來處置它。 當可處置的物件明確處置時,記憶體回收的運作會更有效率。


using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() 
        {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.",
                Thread.CurrentThread.ManagedThreadId);

            // Wait for the WaitHandle to become signaled.
            result.AsyncWaitHandle.WaitOne();

            // Perform additional processing here.
            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            // Close the wait handle.
            result.AsyncWaitHandle.Close();

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

Main thread 1 does some work.
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
 */


您可以使用由 BeginInvoke 傳回之 IAsyncResultIsCompleted 屬性,來探索非同步呼叫完成的時間。 您可能會在從服務使用者介面的執行緒進行非同步呼叫時,執行這項工作。 針對完成而進行的輪詢,能讓呼叫的執行緒繼續執行,並在同時能對 ThreadPool 執行緒執行非同步呼叫。


using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

            // Poll while simulating work.
            while(result.IsCompleted == false) {
                Thread.Sleep(250);
                Console.Write(".");
            }

            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            Console.WriteLine("\nThe call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

Test method begins.
.............
The call executed on thread 3, with return value "My call time was 3000.".
 */


如果啟始非同步呼叫的執行緒,不需要是處理結果的執行緒,您就可以在呼叫完成時執行回呼方法。 回呼方法會在 ThreadPool 執行緒上執行。

若要使用回呼方法,您必須將代表回呼方法的 AsyncCallback 委派,傳遞給 BeginInvoke 您也可以傳遞物件,其中包含要由回呼方法使用的資訊。 在回呼方法中,您可以將回呼方法的唯一參數 IAsyncResult 轉換成 AsyncResult 物件。 您接著可以使用 AsyncResult.AsyncDelegate 屬性取得用來啟始呼叫的委派,以便能夠呼叫 EndInvoke

範例附註:

  • TestMethodthreadId是一個 out 參數 (Visual Basic 裡的<Out> ByRef), 因此 TestMethod永遠不會使用它的輸入值。 空的變數會傳遞到 BeginInvoke 呼叫。 如果 threadId 參數是 ref 參數 (Visual Basic 中的 ByRef),變數就必須是類別層級的欄位,以便能同時傳遞給 BeginInvokeEndInvoke

  • 傳遞給 BeginInvoke 的狀態資訊是格式字串,回呼方法會用來格式化輸出訊息。 狀態資訊是以型別 Object 來傳遞,因此必須先將其轉型為適當型別後才能使用。

  • 回呼會在 ThreadPool 執行緒上執行。 ThreadPool 執行緒是背景執行緒,如果主執行緒結束,就不會使應用程式保持在執行中,因此範例的主執行緒的休眠時間必須夠長,讓回呼得以完成。


using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() 
        {
            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // The threadId parameter of TestMethod is an out parameter, so
            // its input value is never used by TestMethod. Therefore, a dummy
            // variable can be passed to the BeginInvoke call. If the threadId
            // parameter were a ref parameter, it would have to be a class-
            // level field so that it could be passed to both BeginInvoke and 
            // EndInvoke.
            int dummy = 0;

            // Initiate the asynchronous call, passing three seconds (3000 ms)
            // for the callDuration parameter of TestMethod; a dummy variable 
            // for the out parameter (threadId); the callback delegate; and
            // state information that can be retrieved by the callback method.
            // In this case, the state information is a string that can be used
            // to format a console message.
            IAsyncResult result = caller.BeginInvoke(3000,
                out dummy, 
                new AsyncCallback(CallbackMethod),
                "The call executed on thread {0}, with return value \"{1}\".");

            Console.WriteLine("The main thread {0} continues to execute...", 
                Thread.CurrentThread.ManagedThreadId);

            // The callback is made on a ThreadPool thread. ThreadPool threads
            // are background threads, which do not keep the application running
            // if the main thread ends. Comment out the next line to demonstrate
            // this.
            Thread.Sleep(4000);

            Console.WriteLine("The main thread ends.");
        }

        // The callback method must have the same signature as the
        // AsyncCallback delegate.
        static void CallbackMethod(IAsyncResult ar) 
        {
            // Retrieve the delegate.
            AsyncResult result = (AsyncResult) ar;
            AsyncMethodCaller caller = (AsyncMethodCaller) result.AsyncDelegate;

            // Retrieve the format string that was passed as state 
            // information.
            string formatString = (string) ar.AsyncState;

            // Define a variable to receive the value of the out parameter.
            // If the parameter were ref rather than out then it would have to
            // be a class-level field so it could also be passed to BeginInvoke.
            int threadId = 0;

            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, ar);

            // Use the format string to format the output message.
            Console.WriteLine(formatString, threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

The main thread 1 continues to execute...
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
The main thread ends.
 */


社群新增項目

新增
顯示:
© 2015 Microsoft