使用异步方式调用同步方法Calling Synchronous Methods Asynchronously

使用 .NET Framework 可以以异步方式调用任何方法。The .NET Framework enables you to call any method asynchronously. 要实现此操作,请定义一个委托,此委托具有与你要调用的方法相同的签名;公共语言运行时会自动使用适当的签名为此委托定义 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 返回可用于监视异步调用的进度的 IAsyncResultBeginInvoke 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 或类似工具,或者如果使用的是包含 Visual Studio 的 C#,请参阅异步编程模型 (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. 要编译此代码示例,必须包括 TestMethodAsyncMethodCaller 委托的定义。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