Note: 本篇主要内容来自Jeffery Richard的Implementing the CLR Asynchronous Programming Model,看过的同学可以略过。
在前一篇中介绍了使用APM开发多线程程序的有点,同时关于如何使用APM方式的文章也很多了。所以,这篇主要想看看如何使用Jeffery Richard的Power Threading类库,来开发一个支持APM的类。
为什么要自己实现APM
前一篇中,提到了受计算约束(Computing-Bound)与受I/O约束(I/O-Bound)的区别。如果我们是一个受计算约束类型的任务,完全可以将这个方法变成委托(Delegate)的形式,然后通过BeginInvoke/EndInvoke来实现APM;另外,FCL本身也封装了大量对I/O异步操作的类,如FileStream,NetworkStream等,那我们为什么还要自己实现APM呢。Jeffery Richard列举了4个原因:
- 尽管FCL提供很多与设备通讯的异步类,但仍有部分未提供,如并口;或者FCL提供的功能不够
- 对现有的I/O类库添加功能,如我们在WebHttpRequest可以开发一个HTTP的过滤类等
- 实现受计算约束的APM
- 对不支持异步模式设备提供异步访问方式
PowerThreading类库中的AsyncResult和AsyncResult<T>
APM的核心就是IAsyncResult接口。当调用类异步方式BeginXxx时,返回IAsyncResult的对象。为了返回满足这个接口的对象,我们的类需要维护的一个 是否完成的状态IsCompleted;一个ManuelResetEvent对象及一个委托保持回调函数。这些虽然比较简单,但很麻烦。使用PowerThreading类库中AsyncResult和AsyncResult<T>,我们可以非常容易的实现支持APM的类。
AsyncResult用于没有返回值,而AsyncResult<T>用于返回类型为T的任务。下面的例子中,我们看看如何使用AsyncResult<T>。
首先,我们准备实现一个能够过滤Stream对象的类,类定义如下:
1
class
StreamFilter
2
{
3
private
Stream m_stream;
4
public
StreamFilter(Stream stream)
5
{
6
}
7
8
public
IAsyncResult BeginParse(
9
AsyncCallback callback, Object state)
10
{
11
}
12
13
public
Stream Parse()
14
{
15
}
16
17
public
Stream EndParse(IAsyncResult asyncResult)
18
{
19
}
20
}
Parse()是同步访问接口,执行主要的任务,而BeginParse()和EndParse()是对应的异步接口。下面是使用AsyncResult<T>后的代码
1
class
StreamFilter
2
{
3
private
Stream m_stream;
4
public
StreamFilter(Stream stream)
5
{
6
m_stream
=
stream;
7
}
8
9
public
IAsyncResult BeginParse(
10
AsyncCallback callback, Object state)
11
{
12
//
创建一个IAsyncResult对象来标志异步操作
13
AsyncResult
<
Stream
>
ar
=
14
new
AsyncResult
<
Stream
>
(callback, state);
15
16
//
调用辅助函数,并传递AsycnResult对象
17
//
如果使用Anonymouse Method更简洁
18
ThreadPool.QueueUserWorkItem(ParseHelper,ar);
19
20
return
ar;
//
返回IAsyncResult
21
}
22
23
private
void
ParseHelper(Object asyncResult)
24
{
25
var ar
=
(AsyncResult
<
Stream
>
)asyncResult;
26
try
27
{
28
//
执行真正的任务
29
Stream result
=
Parse();
30
31
//
更新AsycnResult状态
32
ar.SetAsCompleted(result,
false
);
33
}
catch
(Exception e)
34
{
35
//
保存Exception对象
36
ar.SetAsCompleted(e,
false
);
37
}
38
}
39
40
public
Stream Parse()
41
{
42
//
分析m_stream,并返回过滤后的stream对象
43
Stream filterStream
=
null
;
44
return
filterStream;
45
}
46
47
public
Stream EndParse(IAsyncResult asyncResult)
48
{
49
var ar
=
(AsyncResult
<
Stream
>
)asyncResult;
50
//
等待任务完成
51
return
ar.EndInvoke();
52
}
53
}
比较重要的是ParseHelper函数,这个函数在新的线程中执行,通过ar.SetAsCompleted来更新状态。 如果结合匿名方法或者Limbda表达式,代码可以变得更加的紧凑。
参考:
Implementing the CLR Asynchronous Programming Model by Jeffery Richard
Power Threading类库