使用 .NET 异步编程,在程序继续执行的同时对 .NET 类方法进行调用,直到进行指定的回调为止;或者如果没有提供回调,则直到对调用的阻塞、轮询或等待完成为止。例如,一个程序可以调用一个方法,该方法枚举一个较大的列表,同时主程序将继续执行。在完成枚举后,进行回调并由程序对它进行寻址。
异步编程是由 .NET Framework 的许多区域支持的功能,这些区域包括:
- 文件 IO、流 IO、套接字 IO
- 网络:HTTP、TCP
- 远程处理信道(HTTP、TCP)和代理
- 使用 ASP.NET 创建的 XML Web services
- ASP.NET Web 窗体
- 使用 MessageQueue 类的消息队列
- 异步委托
本节内容
-
异步编程概述
- 提供代码示例,演示异步调用普通同步方法的四种方式。 异步编程设计模式
- 讨论异步设计模式,这些设计模式提供有效和一致的编程模型来处理异步操作。 异步委托
- 描述异步委托,这些异步委托提供以异步方式调用同步方法的能力。
相关章节
-
AsyncCallback 委托
- 讨论 AsyncCallback 委托编程参考。
------------------------------------------------------------------------------------------------------------------------------------
NET Framework 允许您异步调用任何方法。定义与您需要调用的方法具有相同签名的委托;公共语言运行库将自动为该委托定义具有适当签名的 BeginInvoke 和 EndInvoke 方法。
BeginInvoke 方法用于启动异步调用。它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数(将在稍后描述)。BeginInvoke 立即返回,不等待异步调用完成。BeginInvoke 返回 IasyncResult,可用于监视调用进度。
EndInvoke 方法用于检索异步调用结果。调用 BeginInvoke 后可随时调用 EndInvoke 方法;如果异步调用未完成,EndInvoke 将一直阻塞到异步调用完成。EndInvoke 的参数包括您需要异步执行的方法的 out 和 ref 参数(在 Visual Basic 中为 <Out> ByRef 和 ByRef)以及由 BeginInvoke 返回的 IAsyncResult。
注意 Visual Studio .NET 中的智能感知功能会显示 BeginInvoke 和 EndInvoke 的参数。如果您没有使用 Visual Studio 或类似的工具,或者您使用的是 C# 和 Visual Studio .NET,请参见 异步方法签名获取有关运行库为这些方法定义的参数的描述。
本主题中的代码演示了四种使用 BeginInvoke 和 EndInvoke 进行异步调用的常用方法。调用了 BeginInvoke 后,可以:
- 进行某些操作,然后调用 EndInvoke 一直阻塞到调用完成。
- 使用 IAsyncResult.AsyncWaitHandle 获取 WaitHandle,使用它的 WaitOne 方法将执行一直阻塞到发出 WaitHandle 信号,然后调用 EndInvoke。
- 轮询由 BeginInvoke 返回的 IAsyncResult,确定异步调用何时完成,然后调用 EndInvoke。
- 将用于回调方法的委托传递给 BeginInvoke。该方法在异步调用完成后在 ThreadPool 线程上执行,它可以调用 EndInvoke。
警告 始终在异步调用完成后调用 EndInvoke。
测试方法和异步委托
四个示例全部使用同一个长期运行的测试方法 TestMethod。该方法显示一个表明它已开始处理的控制台信息,休眠几秒钟,然后结束。TestMethod 有一个 out 参数(在 Visual Basic 中为 <Out> ByRef),它演示了如何将这些参数添加到 BeginInvoke 和 EndInvoke 的签名中。您可以用类似的方式处理 ref 参数(在 Visual Basic 中为 ByRef)。
下面的代码示例显示 TestMethod 以及代表 TestMethod 的委托;若要使用任一示例,请将示例代码追加到这段代码中。
注意 为了简化这些示例, TestMethod 在独立于Main()
的类中声明。或者, TestMethod 可以是包含Main()
的同一类中的 static 方法(在 Visual Basic 中为 Shared)。
[Visual Basic] Imports System Imports System.Threading Imports System.Runtime.InteropServices 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 = AppDomain.GetCurrentThreadId() return "MyCallTime was " + callDuration.ToString() End Function End Class ' The delegate must have the same signature as the method ' you want to call asynchronously. Public Delegate Function AsyncDelegate(ByVal callDuration As Integer, _ <Out> ByRef threadId As Integer) As String [C#] using System; using System.Threading; 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 = AppDomain.GetCurrentThreadId(); return "MyCallTime was " + callDuration.ToString(); } } // The delegate must have the same signature as the method // you want to call asynchronously. public delegate string AsyncDelegate(int callDuration, out int threadId);
使用 EndInvoke 等待异步调用
异步执行方法的最简单方式是以 BeginInvoke 开始,对主线程执行一些操作,然后调用 EndInvoke。EndInvoke 直到异步调用完成后才返回。这种技术非常适合文件或网络操作,但是由于它阻塞 EndInvoke,所以不要从用户界面的服务线程中使用它。
[Visual Basic] 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 dlgt As New AsyncDelegate(AddressOf ad.TestMethod) ' Initiate the asynchronous call. Dim ar As IAsyncResult = dlgt.BeginInvoke(3000, _ threadId, Nothing, Nothing) Thread.Sleep(0) Console.WriteLine("Main thread {0} does some work.", _ AppDomain.GetCurrentThreadId()) ' Call EndInvoke to Wait for the asynchronous call to complete, ' and to retrieve the results. Dim ret As String = dlgt.EndInvoke(threadId, ar) Console.WriteLine("The call executed on thread {0}, with return value ""{1}"".", threadId, ret) End Sub End Class [C#] public class AsyncMain { static void Main(string[] args) { // The asynchronous method puts the thread id here. int threadId; // Create an instance of the test class. AsyncDemo ad = new AsyncDemo(); // Create the delegate. AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod); // Initiate the asychronous call. IAsyncResult ar = dlgt.BeginInvoke(3000, out threadId, null, null); Thread.Sleep(0); Console.WriteLine("Main thread {0} does some work.", AppDomain.GetCurrentThreadId()); // Call EndInvoke to Wait for the asynchronous call to complete, // and to retrieve the results. string ret = dlgt.EndInvoke(out threadId, ar); Console.WriteLine("The call executed on thread {0}, with return value /"{1}/".", threadId, ret); } }
使用 WaitHandle 等待异步调用
等待 WaitHandle 是一项常用的线程同步技术。您可以使用由 BeginInvoke 返回的 IAsyncResult 的 AsyncWaitHandle 属性来获取 WaitHandle。异步调用完成时会发出 WaitHandle 信号,而您可以通过调用它的 WaitOne 等待它。
如果您使用 WaitHandle,则在异步调用完成之后,但在通过调用 EndInvoke 检索结果之前,可以执行其他处理。
[Visual Basic] 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 dlgt As New AsyncDelegate(AddressOf ad.TestMethod) ' Initiate the asynchronous call. Dim ar As IAsyncResult = dlgt.BeginInvoke(3000, threadId, Nothing, Nothing) Thread.Sleep(0) Console.WriteLine("Main thread {0} does some work.", AppDomain.GetCurrentThreadId()) ' Wait for the WaitHandle to become signaled. ar.AsyncWaitHandle.WaitOne() ' Perform additional processing here. ' Call EndInvoke to retrieve the results. Dim ret As String = dlgt.EndInvoke(threadId, ar) Console.WriteLine("The call executed on thread {0}, with return value ""{1}"".", threadId, ret) End Sub End Class [C#] public class AsyncMain { static void Main(string[] args) { // The asynchronous method puts the thread id here. int threadId; // Create an instance of the test class. AsyncDemo ad = new AsyncDemo(); // Create the delegate. AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod); // Initiate the asychronous call. IAsyncResult ar = dlgt.BeginInvoke(3000, out threadId, null, null); Thread.Sleep(0); Console.WriteLine("Main thread {0} does some work.", AppDomain.GetCurrentThreadId()); // Wait for the WaitHandle to become signaled. ar.AsyncWaitHandle.WaitOne(); // Perform additional processing here. // Call EndInvoke to retrieve the results. string ret = dlgt.EndInvoke(out threadId, ar); Console.WriteLine("The call executed on thread {0}, with return value /"{1}/".", threadId, ret); } }
轮询异步调用完成
您可以使用由 BeginInvoke 返回的 IAsyncResult 的 IsCompleted 属性来发现异步调用何时完成。从用户界面的服务线程中进行异步调用时可以执行此操作。轮询完成允许用户界面线程继续处理用户输入。
[Visual Basic] 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 dlgt As New AsyncDelegate(AddressOf ad.TestMethod) ' Initiate the asynchronous call. Dim ar As IAsyncResult = dlgt.BeginInvoke(3000, threadId, Nothing, Nothing) ' Poll while simulating work. While ar.IsCompleted = False Thread.Sleep(10) End While ' Call EndInvoke to retrieve the results. Dim ret As String = dlgt.EndInvoke(threadId, ar) Console.WriteLine("The call executed on thread {0}, with return value ""{1}"".", threadId, ret) End Sub End Class [C#] public class AsyncMain { static void Main(string[] args) { // The asynchronous method puts the thread id here. int threadId; // Create an instance of the test class. AsyncDemo ad = new AsyncDemo(); // Create the delegate. AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod); // Initiate the asychronous call. IAsyncResult ar = dlgt.BeginInvoke(3000, out threadId, null, null); // Poll while simulating work. while(ar.IsCompleted == false) { Thread.Sleep(10); } // Call EndInvoke to retrieve the results. string ret = dlgt.EndInvoke(out threadId, ar); Console.WriteLine("The call executed on thread {0}, with return value /"{1}/".", threadId, ret); } }
异步调用完成时执行回调方法
如果启动异步调用的线程不需要处理调用结果,则可以在调用完成时执行回调方法。回调方法在 ThreadPool 线程上执行。
要使用回调方法,必须将代表该方法的 AsyncCallback 委托传递给 BeginInvoke。也可以传递包含回调方法将要使用的信息的对象。例如,可以传递启动调用时曾使用的委托,以便回调方法能够调用 EndInvoke。
[Visual Basic] Public Class AsyncMain ' The asynchronous method puts the thread id here. Private Shared threadId As Integer Shared Sub Main() ' Create an instance of the test class. Dim ad As New AsyncDemo() ' Create the delegate. Dim dlgt As New AsyncDelegate(AddressOf ad.TestMethod) ' Initiate the asynchronous call. Dim ar As IAsyncResult = dlgt.BeginInvoke(3000, _ threadId, _ AddressOf CallbackMethod, _ dlgt) Console.WriteLine("Press Enter to close application.") Console.ReadLine() End Sub ' Callback method must have the same signature as the ' AsyncCallback delegate. Shared Sub CallbackMethod(ByVal ar As IAsyncResult) ' Retrieve the delegate. Dim dlgt As AsyncDelegate = CType(ar.AsyncState, AsyncDelegate) ' Call EndInvoke to retrieve the results. Dim ret As String = dlgt.EndInvoke(threadId, ar) Console.WriteLine("The call executed on thread {0}, with return value ""{1}"".", threadId, ret) End Sub End Class [C#] public class AsyncMain { // Asynchronous method puts the thread id here. private static int threadId; static void Main(string[] args) { // Create an instance of the test class. AsyncDemo ad = new AsyncDemo(); // Create the delegate. AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod); // Initiate the asychronous call. Include an AsyncCallback // delegate representing the callback method, and the data // needed to call EndInvoke. IAsyncResult ar = dlgt.BeginInvoke(3000, out threadId, new AsyncCallback(CallbackMethod), dlgt ); Console.WriteLine("Press Enter to close application."); Console.ReadLine(); } // Callback method must have the same signature as the // AsyncCallback delegate. static void CallbackMethod(IAsyncResult ar) { // Retrieve the delegate. AsyncDelegate dlgt = (AsyncDelegate) ar.AsyncState; // Call EndInvoke to retrieve the results. string ret = dlgt.EndInvoke(out threadId, ar); Console.WriteLine("The call executed on thread {0}, with return value /"{1}/".", threadId, ret); } }