《CLR Via C# 第3版》笔记之(二十二) - APM和EAP

本文涉及的产品
云拨测,每月3000次拨测额度
简介:

APM的介绍请参见《CLR Via C# 第3版》笔记之(二十一) - 异步编程模型(APM)

EAP是Event-based Asynchronous Pattern的缩写,指基于事件的异步模式。

主要内容:

  • EAP和APM的比较
  • APM转换为Task
  • EAP转换为Task

1. EAP和APM的比较

EAP是基于事件的异步模型,比如winform中很多事件***Changing,***Changed,***Completed等等。

这里的异步是指这些事件方法(即***Changing方法,***Completed方法等)是被异步调用的。

但是这些事件方法执行时是同步的,比如***Changing方法执行时,UI是无响应的。

所以我们遇到处理时间较长的操作时,应该让这些事件方法尽快返回,在事件方法中将那些耗时的操作通过其他异步方式调用(比如多线程,Task等)。

Jeffrey大牛喜爱APM的方式的异步对EAP方式不太感冒。

我本身觉得APM和EAP各有自己的优点,可能是应用场景不同。以下是我自己的一些理解:

 

应用场景

资源消耗

回调方法

EAP

适合事件驱动的程序(比如GUI程序)的开发,利用EAP配合visual studio可以很方便的实现各种功能

内部封装较多,占用资源较多

可以在事件方法中用【+=】或【-=】注册和注销多个回调方法。

事件触发时,所有注册的回调的方法都会被执行,动态调用回调方法较困难

APM

适用于服务端程序,利用Begin***和End***,可以应对大量的请求

占用资源少

可以动态的调用指定的回调方法

 

下面用个简单的例子来展现EAP和APM是如何实现异步的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
using  System;
using  System.Windows.Forms;
using  System.Net;
using  System.Threading;
 
class  CLRviaCSharp_22
{
     static  void  Main( string [] args)
     {
         MyFormEAP eap = new  MyFormEAP();
         eap.ShowDialog();
 
         MyFormAPM apm = new  MyFormAPM();
         apm.ShowDialog();
     }
}
 
internal  class  MyFormEAP : Form
{
     public  MyFormEAP()
     {
         Text = "(EAP)Click in the window to start a web request" ;
         Width = 800;
         Height = 600;
     }
 
     protected  override  void  OnClick(EventArgs e)
     {
         // 开始异步web请求
         Text = "(EAP)web request initilized" ;
         var  client = new  WebClient();
         client.DownloadStringCompleted += ProcessString;
 
         client.DownloadStringAsync( new  Uri( "http://www.baidu.com" ));
         base .OnClick(e);
     }
 
     private  void  ProcessString( object  sender, DownloadStringCompletedEventArgs e)
     {
         Text = "(EAP)Content length: "  + e.Result.Length;
     }
}
 
 
internal  class  MyFormAPM : Form
{
     public  MyFormAPM()
     {
         Text = "(APM)Click in the window to start a web request" ;
         Width = 800;
         Height = 600;
     }
 
     protected  override  void  OnClick(EventArgs e)
     {
         // 开始异步web请求
         Text = "(APM)web request initilized" ;
         var  webreq = WebRequest.Create( "http://www.baidu.com" );
         webreq.BeginGetResponse(SyncContextCallback(ProcessWebResponse), webreq);
         base .OnClick(e);
     }
 
     private  void  ProcessWebResponse(IAsyncResult result)
     {
         var  req = (WebRequest)result.AsyncState;
         using  ( var  res = req.EndGetResponse(result))
         {
             Text = "Content length: "  + res.ContentLength;
         }
     }
 
     private  static  AsyncCallback SyncContextCallback(AsyncCallback callback)
     {
         SynchronizationContext sc = SynchronizationContext.Current;
         // 如果没有同步上下文,直接返回传入的东西
         if  (sc == null ) return  callback;
 
         // 返回一个委托,这个委托将委托Post到捕捉到的SC中
         return  asyncResult => sc.Post(result => callback((IAsyncResult)result), asyncResult);
     }
}

 

从上面的例子也可以看出,在基于事件的操作上,EAP方式的代码要比APM方式简单的多。

实现方式上,

APM方式用Begin***和End***方式实现异步

EAP用一个异步请求(DownloadStringAsync)和完成事件时回调函数的注册(client.DownloadStringCompleted += ProcessString;)来实现异步

 

利用Task的ContinuWith方法,可以APM和EAP转换为一个Task。

下面两节演示如何将上例中的APM方式和EAP方式转换为一个Task。

2. APM转换为Task

主要利用TaskFactory中的FromAsync方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using  System;
using  System.Windows.Forms;
using  System.Net;
using  System.Threading;
using  System.Threading.Tasks;
 
class  CLRviaCSharp_22
{
     static  void  Main( string [] args)
     {
         MyFormAPM apm = new  MyFormAPM();
         apm.ShowDialog();
     }
}
 
internal  class  MyFormAPM : Form
{
     public  MyFormAPM()
     {
         Text = "(APM)Click in the window to start a web request" ;
         Width = 800;
         Height = 600;
     }
 
     protected  override  void  OnClick(EventArgs e)
     {
         // 开始异步web请求
         Text = "(APM)web request initilized" ;
         var  webreq = WebRequest.Create( "http://www.baidu.com" );
 
         Task.Factory.FromAsync<WebResponse>(webreq.BeginGetResponse, webreq.EndGetResponse, null )
             .ContinueWith(task =>
             {
                 var  sc = SynchronizationContext.Current;
                 sc.Post(state => { Text = "(APM)Content length: "  + task.Result.ContentLength; }, null );
             }, TaskContinuationOptions.ExecuteSynchronously);
         base .OnClick(e);
     }
}

这里有个很重要的地方,就是Task的ContinueWith方法里设置的TaskContinuationOptions.ExecuteSynchronously参数。

刚开始我没有设置这个参数,怎么也得不到SynchronizationContext.Current(每次都是null)。所以设置不了Text属性。

 

3. EAP转换为Task

主要利用TaskCompletionSource这个类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
using  System;
using  System.Windows.Forms;
using  System.Net;
using  System.Threading;
using  System.Threading.Tasks;
 
class  CLRviaCSharp_22
{
     static  void  Main( string [] args)
     {
         MyFormEAP eap = new  MyFormEAP();
         eap.ShowDialog();
     }
}
 
internal  class  MyFormEAP : Form
{
     public  MyFormEAP()
     {
         Text = "(EAP)Click in the window to start a web request" ;
         Width = 800;
         Height = 600;
     }
 
     protected  override  void  OnClick(EventArgs e)
     {
         // 开始异步web请求
         Text = "(EAP)web request initilized" ;
         var  client = new  WebClient();
         var  tcs = new  TaskCompletionSource< string >();
 
         // DownloadStringCompleted实在GUI线程上执行的
         client.DownloadStringCompleted += (sender, ea) =>
         {
             tcs.SetResult(ea.Result);
         };
 
         // TaskContinuationOptions.ExecuteSynchronously参数保证了
         // ContinueWith也在GUI线程上
         tcs.Task.ContinueWith(task =>
         {
             Text = "(EAP)Content length: "  + task.Result.Length;
         }, TaskContinuationOptions.ExecuteSynchronously);
 
         client.DownloadStringAsync( new  Uri( "http://www.baidu.com" ));
         base .OnClick(e);
     }
}
标签:  CLR via C#笔记



本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2011/12/01/2270792.html,如需转载请自行联系原作者

目录
相关文章
|
3月前
|
C# Python
C# 笔记1 - 操作目录
C# 笔记1 - 操作目录
29 0
|
3月前
|
C# Python
C# 笔记3 - 重载一系列像python那样的print()方法
C# 笔记3 - 重载一系列像python那样的print()方法
27 1
|
3月前
|
存储 C# C++
C# 笔记2 - 数组、集合与与文本文件处理
C# 笔记2 - 数组、集合与与文本文件处理
46 0
|
10月前
|
存储 网络协议 Java
C# 快速入门笔记
C# 快速入门笔记
C# 快速入门笔记
|
C# 开发工具
C#滑动拼图验证码实现笔记
C# 是一个现代的、通用的、面向对象的编程语言,它是由微软(Microsoft)开发的,由 Ecma 和 ISO 核准认可的。突发奇想,动手开发一个C#滑动拼图验证码,下面是我开发过程的记录。
C#滑动拼图验证码实现笔记
|
SQL 开发框架 算法
【读书笔记】《Effective C#》50条建议笔记整理
对《Effective C#:改善C#代码的50个有效方法》一书整理的读书笔记。
28071 5
【读书笔记】《Effective C#》50条建议笔记整理
|
C# 数据库
C#编程-65:读取数据库DataReader对象复习笔记
C#编程-65:读取数据库DataReader对象复习笔记
C#编程-65:读取数据库DataReader对象复习笔记
|
C# 数据库
C#编程-65:数据库Command对象复习笔记
C#编程-65:数据库Command对象复习笔记
C#编程-65:数据库Command对象复习笔记
|
C# 数据库
C#编程-65:连接数据库复习笔记
C#编程-65:连接数据库复习笔记
C#编程-65:连接数据库复习笔记
C#编程-64:ADO.NET对象模型复习笔记
C#编程-64:ADO.NET对象模型复习笔记
C#编程-64:ADO.NET对象模型复习笔记