- A+
所属分类:.NET技术
EAP(Event-based Asynchronous Pattern) 是基于事件的异步模式,在 .NET Framework 2.0 中引入。EAP 需要一个有 Async 后缀方法和一个或多个事件。EAP不再推荐用于新开发。
EAP
一个符合 EAP 模式的示例声明如下:
public class AsyncExample { // Synchronous methods. public int Method1(string param); public void Method2(double param); // Asynchronous methods. public void Method1Async(string param); public void Method1Async(string param, object userState); public event Method1CompletedEventHandler Method1Completed; public void Method2Async(double param); public void Method2Async(double param, object userState); public event Method2CompletedEventHandler Method2Completed; public void CancelAsync(object userState); public bool IsBusy { get; } // Class implementation not shown. }
BackgroundWorker
SoundPlayer和PictureBox组件表示基于事件的异步模式的简单实现。WebClient和BackgroundWorker组件代表了基于事件的异步模式的更复杂的实现。
private void EAP_Btn_Click(object sender, RoutedEventArgs e) { // 记录时间 Debug.WriteLine("1-" + DateTime.Now.TimeOfDay.ToString() + ",ThreadID = " + Thread.CurrentThread.ManagedThreadId); BackgroundWorker worker = new BackgroundWorker(); // 事件绑定 worker.DoWork += Worker_DoWork; worker.RunWorkerCompleted += Worker_RunWorkerCompleted; // 异步执行 worker.RunWorkerAsync(); // 记录时间 Debug.WriteLine("4-" + DateTime.Now.TimeOfDay.ToString() + ",ThreadID = " + Thread.CurrentThread.ManagedThreadId); } private void Worker_RunWorkerCompleted(object? sender, RunWorkerCompletedEventArgs e) { // 记录时间 Debug.WriteLine("3-" + DateTime.Now.TimeOfDay.ToString() + ",ThreadID = " + Thread.CurrentThread.ManagedThreadId); } private void Worker_DoWork(object? sender, DoWorkEventArgs e) { // 访问外网网站网站 var req = WebRequest.Create("https://docs.newrelic.com/docs/apm/agents/net-agent/getting-started/net-agent-compatibility-requirements-net-framework/"); req.GetResponse(); Debug.WriteLine("2-" + DateTime.Now.TimeOfDay.ToString() + ",ThreadID = " + Thread.CurrentThread.ManagedThreadId); }
程序运行效果:
日志输出:
1-17:00:10.3584040,ThreadID = 1 4-17:00:10.3609798,ThreadID = 1 2-17:00:11.3485887,ThreadID = 8 3-17:00:11.3514632,ThreadID = 1
从效果和日志上看:
- EAP 不会阻塞调用线程
- 异步操作真正执行是在另外一个线程
- RunWorkerCompleted 回调会回到调用线程(UI线程)
和APM比起来很像,好像只是把 委托绑定 放到了外面。
我们可以看一下 BackgroundWorker 的源码:
- 在构造函数里实例化一个委托 threadStart
- 调用 RunWorkerAsync() 方法
看起来 EAP 的本质,还是使用了委托的异步方式(BeginInvoke),实质上还是 APM 异步模式。
多任务
如果有多个异步任务,我们希望按照先后顺序执行,并且需要在调用线程上得到所有返回值。
public Func<string, string> func1() { return new Func<string, string>(t => { Thread.Sleep(2000); return "name:" + t; }); } public Func<string, string> func2() { return new Func<string, string>(t => { Thread.Sleep(2000); return "age:" + t; }); } public Func<string, string> func3() { return new Func<string, string>(t => { Thread.Sleep(2000); return "sex:" + t; }); } // 按照一定的顺序去执行 public void Multi_APM_Btn_Click(object sender, RoutedEventArgs e) { string str1 = string.Empty, str2 = string.Empty, str3 = string.Empty; IAsyncResult asyncResult1 = null, asyncResult2 = null, asyncResult3 = null; asyncResult1 = func1().BeginInvoke("张三", t => { str1 = func1().EndInvoke(t); Debug.WriteLine("1-" + DateTime.Now.TimeOfDay.ToString() +",ThreadID = " + Thread.CurrentThread.ManagedThreadId); asyncResult2 = func2().BeginInvoke("26", a => { str2 = func2().EndInvoke(a); Debug.WriteLine("2-" + DateTime.Now.TimeOfDay.ToString() +",ThreadID = " + Thread.CurrentThread.ManagedThreadId); asyncResult3 = func3().BeginInvoke("男", s => { str3 = func3().EndInvoke(s); Debug.WriteLine("3-" + DateTime.Now.TimeOfDay.ToString() + ",ThreadID = " + Thread.CurrentThread.ManagedThreadId); }, null); }, null); }, null); asyncResult1.AsyncWaitHandle.WaitOne(); asyncResult2.AsyncWaitHandle.WaitOne(); asyncResult3.AsyncWaitHandle.WaitOne(); Debug.WriteLine(str1 + str2 + str3); }
运行起来,发现有异常:
由此可见在完成第一个异步操作之前没有对asyncResult2进行赋值,asyncResult2执行异步等待的时候报异常。也可以有其他方法来解决这个问题,但会比较复杂。