.NET异步编程模式(三)

打印 上一主题 下一主题

主题 935|帖子 935|积分 2805

EAP(Event-based Asynchronous Pattern) 是基于事件的异步模式,在 .NET Framework 2.0 中引入。EAP 需要一个有 Async 后缀方法和一个或多个事件。EAP不再推荐用于新开发
EAP

一个符合 EAP 模式的示例声明如下:
  1. public class AsyncExample  
  2. {  
  3.     // Synchronous methods.  
  4.     public int Method1(string param);  
  5.     public void Method2(double param);  
  6.   
  7.     // Asynchronous methods.  
  8.     public void Method1Async(string param);  
  9.     public void Method1Async(string param, object userState);  
  10.     public event Method1CompletedEventHandler Method1Completed;  
  11.   
  12.     public void Method2Async(double param);  
  13.     public void Method2Async(double param, object userState);  
  14.     public event Method2CompletedEventHandler Method2Completed;  
  15.   
  16.     public void CancelAsync(object userState);  
  17.   
  18.     public bool IsBusy { get; }  
  19.   
  20.     // Class implementation not shown.  
  21. }
复制代码
BackgroundWorker

SoundPlayer和PictureBox组件表示基于事件的异步模式的简单实现。WebClient和BackgroundWorker组件代表了基于事件的异步模式的更复杂的实现。
  1. private void EAP_Btn_Click(object sender, RoutedEventArgs e)
  2. {
  3.     // 记录时间
  4.     Debug.WriteLine("1-" + DateTime.Now.TimeOfDay.ToString() +
  5.                     ",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
  6.     BackgroundWorker worker = new BackgroundWorker();
  7.     // 事件绑定
  8.     worker.DoWork += Worker_DoWork;
  9.     worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
  10.     // 异步执行
  11.     worker.RunWorkerAsync();
  12.     // 记录时间
  13.     Debug.WriteLine("4-" + DateTime.Now.TimeOfDay.ToString() +
  14.                     ",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
  15. }
  16. private void Worker_RunWorkerCompleted(object? sender, RunWorkerCompletedEventArgs e)
  17. {
  18.     // 记录时间
  19.     Debug.WriteLine("3-" + DateTime.Now.TimeOfDay.ToString() +
  20.                     ",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
  21. }
  22. private void Worker_DoWork(object? sender, DoWorkEventArgs e)
  23. {
  24.     // 访问外网网站网站
  25.     var req = WebRequest.Create("https://docs.newrelic.com/docs/apm/agents/net-agent/getting-started/net-agent-compatibility-requirements-net-framework/");
  26.     req.GetResponse();
  27.     Debug.WriteLine("2-" + DateTime.Now.TimeOfDay.ToString() +
  28.                     ",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
  29. }
复制代码
程序运行效果:

日志输出:
  1. 1-17:00:10.3584040,ThreadID = 1
  2. 4-17:00:10.3609798,ThreadID = 1
  3. 2-17:00:11.3485887,ThreadID = 8
  4. 3-17:00:11.3514632,ThreadID = 1
复制代码
从效果和日志上看:

  • EAP 不会阻塞调用线程
  • 异步操作真正执行是在另外一个线程
  • RunWorkerCompleted 回调会回到调用线程(UI线程)
和APM比起来很像,好像只是把 委托绑定 放到了外面。
我们可以看一下 BackgroundWorker 的源码:

  • 在构造函数里实例化一个委托 threadStart




  • 调用 RunWorkerAsync() 方法

看起来 EAP 的本质,还是使用了委托的异步方式(BeginInvoke),实质上还是 APM 异步模式。
多任务

如果有多个异步任务,我们希望按照先后顺序执行,并且需要在调用线程上得到所有返回值。
  1. public Func<string, string> func1()
  2. {
  3.     return new Func<string, string>(t =>
  4.     {
  5.         Thread.Sleep(2000);
  6.         return "name:" + t;
  7.     });
  8. }
  9. public Func<string, string> func2()
  10. {
  11.     return new Func<string, string>(t =>
  12.     {
  13.         Thread.Sleep(2000);
  14.         return "age:" + t;
  15.     });
  16. }
  17. public Func<string, string> func3()
  18. {
  19.     return new Func<string, string>(t =>
  20.     {
  21.         Thread.Sleep(2000);
  22.         return "sex:" + t;
  23.     });
  24. }
  25. // 按照一定的顺序去执行
  26. public void Multi_APM_Btn_Click(object sender, RoutedEventArgs e)
  27. {
  28.     string str1 = string.Empty, str2 = string.Empty, str3 = string.Empty;
  29.     IAsyncResult asyncResult1 = null, asyncResult2 = null, asyncResult3 = null;
  30.     asyncResult1 = func1().BeginInvoke("张三", t =>
  31.     {
  32.         str1 = func1().EndInvoke(t);
  33.         Debug.WriteLine("1-" + DateTime.Now.TimeOfDay.ToString() +",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
  34.         asyncResult2 = func2().BeginInvoke("26", a =>
  35.         {
  36.             str2 = func2().EndInvoke(a);
  37.             Debug.WriteLine("2-" + DateTime.Now.TimeOfDay.ToString() +",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
  38.             asyncResult3 = func3().BeginInvoke("男", s =>
  39.             {
  40.                 str3 = func3().EndInvoke(s);
  41.                 Debug.WriteLine("3-" + DateTime.Now.TimeOfDay.ToString() + ",ThreadID = " + Thread.CurrentThread.ManagedThreadId);
  42.             }, null);
  43.         }, null);
  44.     }, null);
  45.     asyncResult1.AsyncWaitHandle.WaitOne();
  46.     asyncResult2.AsyncWaitHandle.WaitOne();
  47.     asyncResult3.AsyncWaitHandle.WaitOne();
  48.     Debug.WriteLine(str1 + str2 + str3);
  49. }
复制代码
运行起来,发现有异常:
由此可见在完成第一个异步操作之前没有对asyncResult2进行赋值,asyncResult2执行异步等待的时候报异常。也可以有其他方法来解决这个问题,但会比较复杂。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

自由的羽毛

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表