以非同步的方式呼叫同步方法Calling Synchronous Methods Asynchronously

.NET Framework 可讓您以非同步方式呼叫任何方法。The .NET Framework enables you to call any method asynchronously. 若要這樣做,您使用相同簽章定義委派,做為您要呼叫的方法;Common Language Runtime 則會自動以適當簽章定義此委派的 BeginInvokeEndInvoke 方法。To do this you define a delegate with the same signature as the method you want to call; the common language runtime automatically defines BeginInvoke and EndInvoke methods for this delegate, with the appropriate signatures.

注意

在 .NET Compact Framework 中不會支援非同步委派呼叫,尤其是 BeginInvokeEndInvoke 方法Asynchronous delegate calls, specifically the BeginInvoke and EndInvoke methods, are not supported in the .NET Compact Framework.

BeginInvoke 方法會起始非同步呼叫。The BeginInvoke method initiates the asynchronous call. 它有相同參數做為您要非同步執行的方法,再加上兩個額外的選擇性參數。It has the same parameters as the method that you want to execute asynchronously, plus two additional optional parameters. 第一個參數是 AsyncCallback 委派,參考非同步呼叫完成時所呼叫的方法。The first parameter is an AsyncCallback delegate that references a method to be called when the asynchronous call completes. 第二個參數使用者定義的物件,可將資訊傳遞至回呼方法。The second parameter is a user-defined object that passes information into the callback method. BeginInvoke 會立即傳回且不等候非同步呼叫完成。BeginInvoke returns immediately and does not wait for the asynchronous call to complete. BeginInvoke 傳回 IAsyncResult,可用來監視非同步呼叫的進度。BeginInvoke returns an IAsyncResult, which can be used to monitor the progress of the asynchronous call.

EndInvoke 方法會擷取非同步呼叫的結果。The EndInvoke method retrieves the results of the asynchronous call. BeginInvoke 之後隨時可以呼叫它。It can be called any time after BeginInvoke. 如果非同步呼叫尚未完成,在完成前 EndInvoke 會封鎖呼叫執行緒。If the asynchronous call has not completed, EndInvoke blocks the calling thread until it completes. EndInvoke 的參數包括您要非同步執行之方法的 outref 參數 (Visual Basic 為 <Out> ByRefByRef),再加上 BeginInvoke 所傳回的 IAsyncResultThe parameters of EndInvoke include the out and ref parameters (<Out> ByRef and ByRef in Visual Basic) of the method that you want to execute asynchronously, plus the IAsyncResult returned by BeginInvoke.

注意

Visual Studio 中的 IntelliSense 功能會顯示 BeginInvokeEndInvoke 的參數。The IntelliSense feature in Visual Studio displays the parameters of BeginInvoke and EndInvoke. 假如您並非使用 Visual Studio 或類似工具,或您使用 C# 搭配 Visual Studio,請參閱非同步程式設計模型 (APM),以取得針對這些方法所定義的參數說明。If you're not using Visual Studio or a similar tool, or if you're using C# with Visual Studio, see Asynchronous Programming Model (APM) for a description of the parameters defined for these methods.

本主題中的程式碼範例將示範四個使用 BeginInvokeEndInvoke 進行非同步呼叫的常用方法。The code examples in this topic demonstrate four common ways to use BeginInvoke and EndInvoke to make asynchronous calls. 呼叫 BeginInvoke 之後您可執行以下動作:After calling BeginInvoke you can do the following:

  • 執行一些工作,然後呼叫 EndInvoke 進行封鎖,直到呼叫完成。Do some work and then call EndInvoke to block until the call completes.

  • 取得使用 WaitHandle 屬性的 IAsyncResult.AsyncWaitHandle ,利用其 WaitOne 方法封鎖執行,直到 WaitHandle 收到信號,接著呼叫 EndInvokeObtain a WaitHandle using the IAsyncResult.AsyncWaitHandle property, use its WaitOne method to block execution until the WaitHandle is signaled, and then call EndInvoke.

  • 輪詢 IAsyncResult 所傳回的 BeginInvoke ,判斷非同步呼叫完成的時間,然後再呼叫 EndInvokePoll the IAsyncResult returned by BeginInvoke to determine when the asynchronous call has completed, and then call EndInvoke.

  • 將回呼方法的委派傳遞至 BeginInvokePass a delegate for a callback method to BeginInvoke. 非同步呼叫完成之時,會在 ThreadPool 執行緒上執行此方法。The method is executed on a ThreadPool thread when the asynchronous call completes. 回呼方法會呼叫 EndInvokeThe callback method calls EndInvoke.

重要

不論您使用哪一種技術,請務必呼叫 EndInvoke 完成非同步呼叫。No matter which technique you use, always call EndInvoke to complete your asynchronous call.

定義測試方法和非同步委派Defining the Test Method and Asynchronous Delegate

以下程式碼範例示範非同步呼叫同一個長時間執行之方法 TestMethod 的各種方式。The code examples that follow demonstrate various ways of calling the same long-running method, TestMethod, asynchronously. TestMethod 方法會顯示主控台訊息,告訴您它已經開始處理、睡眠數秒鐘,然後結束。The TestMethod method displays a console message to show that it has begun processing, sleeps for a few seconds, and then ends. TestMethod 具有 out 參數,以示範這類參數加入 BeginInvokeEndInvoke 簽章的方法。TestMethod has an out parameter to demonstrate the way such parameters are added to the signatures of BeginInvoke and EndInvoke. 您可以用類似方式處理 ref 參數。You can handle ref parameters similarly.

下列程式碼範例顯示 TestMethod 的定義及名為 AsyncMethodCaller 且可用來非同步呼叫 TestMethod 的委派。The following code example shows the definition of TestMethod and the delegate named AsyncMethodCaller that can be used to call TestMethod asynchronously. 若要編譯程式碼範例,您必須包含 TestMethod 的定義和 AsyncMethodCaller 委派。To compile the code examples, you must include the definitions for TestMethod and the AsyncMethodCaller delegate.

using namespace System;
using namespace System::Threading;
using namespace System::Runtime::InteropServices; 

namespace Examples {
namespace AdvancedProgramming {
namespace AsynchronousOperations
{
    public ref class AsyncDemo 
    {
    public:
        // The method to be executed asynchronously.
        String^ TestMethod(int callDuration, [OutAttribute] int% threadId) 
        {
            Console::WriteLine("Test method begins.");
            Thread::Sleep(callDuration);
            threadId = Thread::CurrentThread->ManagedThreadId;
            return String::Format("My call time was {0}.", callDuration);
        }
    };

    // The delegate must have the same signature as the method
    // it will call asynchronously.
    public delegate String^ AsyncMethodCaller(int callDuration, [OutAttribute] int% threadId);
}}}
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);
}
Imports System.Threading
Imports System.Runtime.InteropServices 

Namespace Examples.AdvancedProgramming.AsynchronousOperations
    Public Class AsyncDemo 
        ' The method to be executed asynchronously.
        Public Function TestMethod(ByVal callDuration As Integer, _
                <Out> ByRef threadId As Integer) As String
            Console.WriteLine("Test method begins.")
            Thread.Sleep(callDuration)
            threadId = Thread.CurrentThread.ManagedThreadId()
            return String.Format("My call time was {0}.", callDuration.ToString())
        End Function
    End Class

    ' The delegate must have the same signature as the method
    ' it will call asynchronously.
    Public Delegate Function AsyncMethodCaller(ByVal callDuration As Integer, _
        <Out> ByRef threadId As Integer) As String
End Namespace

使用 EndInvoke 等候非同步呼叫Waiting for an Asynchronous Call with EndInvoke

以非同步方式執行方法最簡單的方式,就是以呼叫委派的 BeginInvoke 方法來開始執行方法,在主執行緒上執行相同的工作,然後呼叫委派的 EndInvoke 方法。The simplest way to execute a method asynchronously is to start executing the method by calling the delegate's BeginInvoke method, do some work on the main thread, and then call the delegate's EndInvoke method. EndInvoke 有可能會封鎖呼叫執行緒,因為它在非同赴呼叫完成之前都不會傳回。EndInvoke might block the calling thread because it does not return until the asynchronous call completes. 這個技術非常適合運用於檔案或網路作業。This is a good technique to use with file or network operations.

重要

由於 EndInvoke 可能會進行封鎖,因此請您千萬不要從服役於使用者介面的執行緒中呼叫它。Because EndInvoke might block, you should never call it from threads that service the user interface.

#using <TestMethod.dll>

using namespace System;
using namespace System::Threading;
using namespace Examples::AdvancedProgramming::AsynchronousOperations;

void main() 
{
    // The asynchronous method puts the thread id here.
    int threadId = 2546;

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

    // Create the delegate.
    AsyncMethodCaller^ caller = gcnew AsyncMethodCaller(ad, &AsyncDemo::TestMethod);
       
    // Initiate the asychronous call.
    IAsyncResult^ result = caller->BeginInvoke(3000, 
        threadId, nullptr, nullptr);

    Thread::Sleep(1);
    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(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.".
 */
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.".
 */
Imports System.Threading
Imports System.Runtime.InteropServices 

Namespace Examples.AdvancedProgramming.AsynchronousOperations
    Public Class AsyncMain 
        Shared Sub Main() 
            ' The asynchronous method puts the thread id here.
            Dim threadId As Integer

            ' Create an instance of the test class.
            Dim ad As New AsyncDemo()

            ' Create the delegate.
            Dim caller As New AsyncMethodCaller(AddressOf ad.TestMethod)
       
            ' Initiate the asynchronous call.
            Dim result As IAsyncResult = caller.BeginInvoke(3000, _
                threadId, Nothing, Nothing)

            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.
            Dim returnValue As String = caller.EndInvoke(threadId, result)

            Console.WriteLine("The call executed on thread {0}, with return value ""{1}"".", _
                threadId, returnValue)
        End Sub
    End Class

End Namespace

'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.".

使用 WaitHandle 等候非同步呼叫Waiting for an Asynchronous Call with WaitHandle

您可以使用取得 WaitHandle 所傳回之 AsyncWaitHandleIAsyncResult 屬性,取得 BeginInvokeYou can obtain a WaitHandle by using the AsyncWaitHandle property of the IAsyncResult returned by BeginInvoke. 非同步呼叫完成時, WaitHandle 會收到信號,且您可以呼叫 WaitOne 方法來等候它。The WaitHandle is signaled when the asynchronous call completes, and you can wait for it by calling the WaitOne method.

如果您使用 WaitHandle,您可以在非同步呼叫完成之前或之後,且在呼叫 EndInvoke 擷取結果之前,執行其他的處理。If you use a WaitHandle, you can perform additional processing before or after the asynchronous call completes, but before calling EndInvoke to retrieve the results.

注意

當您呼叫 EndInvoke時,等候控制代碼不會自動關閉。The wait handle is not closed automatically when you call EndInvoke. 如果您釋放所有等候控制代碼的參考,當記憶體回收收回等候控制代碼時,系統資源就會釋放。If you release all references to the wait handle, system resources are freed when garbage collection reclaims the wait handle. 若要在您使用完等候控制代碼後立即釋放系統資源,請呼叫 WaitHandle.Close 方法來加以配置。To free the system resources as soon as you are finished using the wait handle, dispose of it by calling the WaitHandle.Close method. 明確配置可處置的物件之後,記憶體回收會更有效率。Garbage collection works more efficiently when disposable objects are explicitly disposed.

#using <TestMethod.dll>

using namespace System;
using namespace System::Threading;
using namespace Examples::AdvancedProgramming::AsynchronousOperations;

void main() 
{
    // The asynchronous method puts the thread id here.
    int threadId;

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

    // Create the delegate.
    AsyncMethodCaller^ caller = gcnew AsyncMethodCaller(ad, &AsyncDemo::TestMethod);
       
    // Initiate the asychronous call.
    IAsyncResult^ result = caller->BeginInvoke(3000, 
        threadId, nullptr, nullptr);

    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(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.".
 */
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.".
 */
Imports System.Threading
Imports System.Runtime.InteropServices 

Namespace Examples.AdvancedProgramming.AsynchronousOperations

    Public Class AsyncMain 
        Shared Sub Main() 
            ' The asynchronous method puts the thread id here.
            Dim threadId As Integer

            ' Create an instance of the test class.
            Dim ad As New AsyncDemo()

            ' Create the delegate.
            Dim caller As New AsyncMethodCaller(AddressOf ad.TestMethod)
       
            ' Initiate the asynchronous call.
            Dim result As IAsyncResult = caller.BeginInvoke(3000, _
                threadId, Nothing, Nothing)

            Thread.Sleep(0)
            Console.WriteLine("Main thread {0} does some work.", _
                Thread.CurrentThread.ManagedThreadId)
            ' Perform additional processing here and then
            ' wait for the WaitHandle to be signaled.
            result.AsyncWaitHandle.WaitOne()

            ' Call EndInvoke to retrieve the results.
            Dim returnValue As String = caller.EndInvoke(threadId, result)

            ' Close the wait handle.
            result.AsyncWaitHandle.Close()

            Console.WriteLine("The call executed on thread {0}, with return value ""{1}"".", _
                threadId, returnValue)
        End Sub
    End Class
End Namespace

'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.".

輪詢是否完成非同步呼叫Polling for Asynchronous Call Completion

您可以使用 IsCompleted 所傳回之 IAsyncResultBeginInvoke 屬性,找出非同步呼叫完成的時間。You can use the IsCompleted property of the IAsyncResult returned by BeginInvoke to discover when the asynchronous call completes. 從服役於使用者介面的執行緒進行非同步呼叫時,您有可能會執行這項操作。You might do this when making the asynchronous call from a thread that services the user interface. 輪詢完成這個動作,可在非同步呼叫於 ThreadPool 執行緒上執行的同時,讓呼叫執行緒能夠繼續執行。Polling for completion allows the calling thread to continue executing while the asynchronous call executes on a ThreadPool thread.

#using <TestMethod.dll>

using namespace System;
using namespace System::Threading;
using namespace Examples::AdvancedProgramming::AsynchronousOperations;

void main() 
{
    // The asynchronous method puts the thread id here.
    int threadId;

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

    // Create the delegate.
    AsyncMethodCaller^ caller = gcnew AsyncMethodCaller(ad, &AsyncDemo::TestMethod);
       
    // Initiate the asychronous call.
    IAsyncResult^ result = caller->BeginInvoke(3000, 
        threadId, nullptr, nullptr);

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

    // Call EndInvoke to retrieve the results.
    String^ returnValue = caller->EndInvoke(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.".
 */
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.".
 */
Imports System.Threading
Imports System.Runtime.InteropServices 

Namespace Examples.AdvancedProgramming.AsynchronousOperations

    Public Class AsyncMain 
        Shared Sub Main() 
            ' The asynchronous method puts the thread id here.
            Dim threadId As Integer

            ' Create an instance of the test class.
            Dim ad As New AsyncDemo()

            ' Create the delegate.
            Dim caller As New AsyncMethodCaller(AddressOf ad.TestMethod)
       
            ' Initiate the asynchronous call.
            Dim result As IAsyncResult = caller.BeginInvoke(3000, _
                threadId, Nothing, Nothing)

            ' Poll while simulating work.
            While result.IsCompleted = False
                Thread.Sleep(250)
                Console.Write(".")
            End While

            ' Call EndInvoke to retrieve the results.
            Dim returnValue As String = caller.EndInvoke(threadId, result)

            Console.WriteLine(vbCrLf & _
                "The call executed on thread {0}, with return value ""{1}"".", _
                threadId, returnValue)
        End Sub
    End Class
End Namespace

' 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.".

當非同步呼叫完成時執行回呼方法Executing a Callback Method When an Asynchronous Call Completes

如果起始非同步呼叫的執行緒不一定非是處理結果的執行緒時,您可以在呼叫完成時執行回呼方法。If the thread that initiates the asynchronous call does not need to be the thread that processes the results, you can execute a callback method when the call completes. 回呼方法是在 ThreadPool 執行緒上執行的。The callback method is executed on a ThreadPool thread.

若要使用回呼方法,您必須對 BeginInvoke 傳遞表示回呼方法的 AsyncCallback 委派。To use a callback method, you must pass BeginInvoke an AsyncCallback delegate that represents the callback method. 您也可以傳遞物件,包含回呼方法所使用的資訊。You can also pass an object that contains information to be used by the callback method. 在回呼方法中,您可以將 IAsyncResult這個回呼方法唯一的參數,轉換成 AsyncResult 物件。In the callback method, you can cast the IAsyncResult, which is the only parameter of the callback method, to an AsyncResult object. 接著可以再使用 AsyncResult.AsyncDelegate 屬性來取得委派,用來起使呼叫,以便您呼叫 EndInvokeYou can then use the AsyncResult.AsyncDelegate property to get the delegate that was used to initiate the call so that you can call EndInvoke.

此範例的注意事項如下:Notes on the example:

  • TestMethodthreadId 參數是 out 參數 (在 Visual Basic 中為 [<Out> ByRef),因此 TestMethod 絕對不會使用它的輸入值。The threadId parameter of TestMethod is an out parameter ([<Out> ByRef in Visual Basic), so its input value is never used by TestMethod. 虛設變數會傳遞給 BeginInvoke 呼叫。A dummy variable is passed to the BeginInvoke call. 如果 threadId 參數是 ref 參數 (Visual Basic 中的 ByRef),則變數必須是類別層級的欄位,才能同時傳遞至 BeginInvokeEndInvokeIf the threadId parameter were a ref parameter (ByRef in Visual Basic), the variable would have to be a class-level field so that it could be passed to both BeginInvoke and EndInvoke.

  • 傳遞至 BeginInvoke 的狀態資訊是格式字串,回呼方法會用它來格式化輸出訊息。The state information that is passed to BeginInvoke is a format string, which the callback method uses to format an output message. 由於狀態資訊傳遞時的型別是 Object,因此必須先轉換成適當型別才能使用。Because it is passed as type Object, the state information must be cast to its proper type before it can be used.

  • 回呼是在 ThreadPool 執行緒上執行的。The callback is made on a ThreadPool thread. ThreadPool 執行緒為背景執行緒,也就是說主執行緒結束之時,並不會讓應用程式繼續執行,因此此範例的主執行緒必須進入睡眠直到回呼完成為止。ThreadPool threads are background threads, which do not keep the application running if the main thread ends, so the main thread of the example has to sleep long enough for the callback to finish.

#using <TestMethod.dll>

using namespace System;
using namespace System::Threading;
using namespace System::Runtime::Remoting::Messaging;
using namespace Examples::AdvancedProgramming::AsynchronousOperations;

// The callback method must have the same signature as the
// AsyncCallback delegate.
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(threadId, ar);

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

void main() 
{
    // Create an instance of the test class.
    AsyncDemo^ ad = gcnew AsyncDemo();

    // Create the delegate.
    AsyncMethodCaller^ caller = gcnew AsyncMethodCaller(ad, &AsyncDemo::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,
        dummy, 
        gcnew 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.");
}

/* 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.
 */
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.
 */
Imports System.Threading
Imports System.Runtime.Remoting.Messaging

Namespace Examples.AdvancedProgramming.AsynchronousOperations

    Public Class AsyncMain 
        
        Shared Sub Main() 

            ' Create an instance of the test class.
            Dim ad As New AsyncDemo()

            ' Create the delegate.
            Dim caller As New AsyncMethodCaller(AddressOf 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 ByRef parameter, it would have to be a class-
            ' level field so that it could be passed to both BeginInvoke and 
            ' EndInvoke.
            Dim dummy As Integer = 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.
            Dim result As IAsyncResult = caller.BeginInvoke(3000, _
                dummy, _
                AddressOf 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.")
        End Sub

        ' The callback method must have the same signature as the
        ' AsyncCallback delegate.
        Shared Sub CallbackMethod(ByVal ar As IAsyncResult)
            ' Retrieve the delegate.
            Dim result As AsyncResult = CType(ar, AsyncResult)
            Dim caller As AsyncMethodCaller = CType(result.AsyncDelegate, AsyncMethodCaller)

            ' Retrieve the format string that was passed as state 
            ' information.
            Dim formatString As String = CType(ar.AsyncState, String)

            ' Define a variable to receive the value of the <Out> parameter.
            ' If the parameter were ByRef rather than <Out> then it would have to
            ' be a class-level field so it could also be passed to BeginInvoke.
            Dim threadId As Integer = 0

            ' Call EndInvoke to retrieve the results.
            Dim returnValue As String = caller.EndInvoke(threadId, ar)

            ' Use the format string to format the output message.
            Console.WriteLine(formatString, threadId, returnValue)
        End Sub
    End Class
End Namespace

' 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.

請參閱See also