C#异步编程概述 Windows 应用程序编程中常见的一个模式就是,在GUI用户界面下,将耗时的文件和网络处理放入子线程,以避免用户界面不能响应的问题。在.NET出现以前,创建线 程并监视线程结束,还要更新界面等工作,即复杂又要手写大量代码,并且难以调试。在.NET中,程序员可以通过异步调用,使用简单的代码完成这项工作。 .NET Framework允许异步调用任何方法。使用异步调用之前,要定义一个委托,它的方法签名要与调用方法签名一致。.NET会自动产生此委托的BeginInvoke和EndInvoke方法。 BeginInvoke 方法用于启动异步调用。它除了具有调用方法相同的参数外,还附加了两个额外的参数,这两个额外参数将用于回调方法。 BeginInvoke执行后立即返回,并不等待异步调用完成。 BeginInvoke返回一个IAsyncResult接口,它可以用于监视调用的进度。EndInvoke方法被用来获取异步调用的结果。在 BeginInvoke执行以后,任何时间都可以调用EndInvoke。如果异步调用尚未完成,EndInvoke会被阻塞,直到异步调用完成。EndInvoke带有一个IAsyncResult接口类型的参数,并返回一个IAsyncResult接口,用来获取调用结果。 本文中的代码演示了使用BeginInvoke和EndInvoke进行异步调用的四种常见调用方式。在调用BeginInvoke之后,您可以: 1.完成一些其它工作,然后调用EndInvoke,等待异步调用完成。 2.使用IAsyncResult.AsyncWaitHandle获取一个WaitHandle,使用它的WaitOne方法执行阻塞调用,完成后再调用EndInvoke。 3.轮询BeginInvoke返回的IAsyncResult接口,在异步调用完成后,再调用EndInvoke。 4.给BeginInvoke传递一个回调方法的委托,异步调用完成后,这个回调方法会在ThreadPool线程上执行,可以在这个方法内调用EndInvoke。 警告:异步调用完成后,必须调用EndInvoke。 测试方法和异步委托 在以下代码中,都使用同一个耗时的测试方法TestMethod。此方法向控制台输出信息,显示其开始执行,睡眠几秒钟,结束执行。TestMethod包含一个out参数,BeginInvoke和EndInvoke会同样包括它,你将来可以自己添加ref参数。 以下为TestMethod方法和它的委托。 注意:为了便于说明,TestMethod被定义与与main()不同的一个类当中。当然,TestMethod也可以定义为一个与Main()方法同一个类当中的静态方法。 using System; using System.Threading; public class AsyncDemo { // 异步调用方法 public string TestMethod(int callDuration, out int threadId) { Console.WriteLine("Test method begins."); Thread.Sleep(callDuration); threadId = AppDomain.GetCurrentThreadId(); return "MyCallTime was " + callDuration.ToString(); } } // 委托和调用方法有相同的签名 public delegate string AsyncDelegate(int callDuration, out int threadId); 众代表的字符串AsyncDelegate(整数callDuration,OUT INT的ThreadID); 调用EndInvoke,等待异步调用结束 异步调用最简单的方法是从BeginInvoke开始,在主线程中完成一些其它工作,然后调用EndInvoke,直到异步调用结束。对于文件和网络操作,这个技术不错。但是,由于主线程被阻塞于EndInvoke,该技术不适于有用户界面的应用程序。 public class AsyncMain { static void Main(string[] args) { // 异步调用使用的线程id int threadId; // 产生类实例 AsyncDemo ad = new AsyncDemo(); // 产生委托 AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod); // 开始异步调用 IAsyncResult ar = dlgt.BeginInvoke(3000, out threadId, null, null); Thread.Sleep(0); Console.WriteLine("Main thread {0} does some work.", AppDomain.GetCurrentThreadId()); // 调用EndInvoke,阻塞并等待完成 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 属性。通过调用它的WaitOne方法,等待异步调用结束。一旦异步调用结束,还可以有机会完成其它工作,最终调用EndInvoke并获得异步调用结 果。 public class AsyncMain { static void Main(string[] args) { // 异步调用使用的线程id int threadId; // 产生类实例 AsyncDemo ad = new AsyncDemo(); // 产生委托 AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod); // 开始异步调用 IAsyncResult ar = dlgt.BeginInvoke(3000, out threadId, null, null); Thread.Sleep(0); Console.WriteLine("Main thread {0} does some work.", AppDomain.GetCurrentThreadId()); // 等待WaitHandle得到信号 ar.AsyncWaitHandle.WaitOne(); // 可以完成一些其它工作 // 最后一定要调用EndInvoke string ret = dlgt.EndInvoke(out threadId, ar); Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".", threadId, ret); } } 轮询异步调用何时完成 可以使用由BeginInvoke返回的IAsyncResult接口的IsCompleted属性判断调用是否结束。用户界面线程可以轮询异步调用状态,同时又服务于用户输入。 public class AsyncMain { static void Main(string[] args) { // 异步调用使用的线程id int threadId; // 产生类实例 AsyncDemo ad = new AsyncDemo(); // 产生委托 AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod); // 开始异步调用 IAsyncResult ar = dlgt.BeginInvoke(3000, out threadId, null, null); // 轮询 while(ar.IsCompleted == false) { Thread.Sleep(10); } // 最后一定要调用EndInvoke 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。 public class AsyncMain { // Asynchronous method puts the thread id here. private static int threadId; static void Main(string[] args) { // 异步调用使用的线程id int threadId; // 产生类实例 AsyncDemo ad = new AsyncDemo(); // 产生委托 AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod); // 启动异步调用,包含一个AsyncCallback类型的委托,即回调方法,和回调方法使用的参数,即启动异步调用的委托。 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); } } 此文源自MSDN:Asynchronous Programming Overview(http://msdn.microsoft.com/en-us/library/2e08f6yc%28v=vs.71%29.aspx) 译者:孙钢 |
|