基于Power Threading在Compact Framework中实现APM模式
.Net Framework线程介绍
为了能让大家能快速简单的了解当前.Net Framework现有的异步编程模型,请大家参考一下Jeffrey Richter在CLR via C# 3rd中的这个表。
注:AsnycEnumetor是Jeffery Richter在其Power Threading中提供的一个多线程编程模型
本文将只关注IAsyncResult APM相关部分,如有不妥之处请及时指。,如您有兴趣共同探讨其他相关模型,请不吝赐教。
背景
公司某产品需要在打印处理时进行性能重构。当前的打印功能实际上除了真正的借助蓝牙打印机打印外还必须同时生成pdf文档或者html文档。而现有的编程模型都是串行化的,没能充分利用异步线程的优势。
限制
由于重构部分有很多的是关于I/O操作相关的,因此很直接的想到了运用IAsyncResult APM模型。但是等等,.Net Compact Framework没有对IAsyncResult接口的实现,自己实现一个?有兴趣的朋友可以参照以下Jerrey的文章,来自己实现一个。
http://msdn.microsoft.com/en-us/magazine/cc163467.aspx
不过我选择了一个偷懒的办法--引入Power Threading库,其中有Jerrey已经实现了IAsyncResult具体类(对不重新造轮子的的坚持)。
具体实现
废话不多说,直接上代码:
internal sealed class LongTask
{
private Int32 m_ms; // Milliseconds;
public LongTask(Int32 seconds)
{
m_ms = seconds * 1000;
}
// Synchronous version of time-consuming method
public DateTime DoTask()
{
Thread.Sleep(m_ms); // Simulate time-consuming task such as printing
return DateTime.Now; // Indicate when task completed
}
// Asynchronous version of time-consuming method (Begin part)
public IAsyncResult BeginDoTask(AsyncCallback callback, Object state)
{
// Create IAsyncResult object identifying the
// asynchronous operation
AsyncResult<DateTime> ar = new AsyncResult<DateTime>(
callback, state);
// Use a thread pool thread to perform the operation
ThreadPool.QueueUserWorkItem(asyncState=>{
AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;
try
{
// Perform the operation; if sucessful set the result
DateTime dt = DoTask();
ar.SetAsCompleted(dt, false);
}
catch (Exception e)
{
// If operation fails, set the exception
ar.SetAsCompleted(e, false);
}
}, ar);
return ar; // Return the IAsyncResult to the caller
}
// Asynchronous version of time-consuming method (End part)
public DateTime EndDoTask(IAsyncResult asyncResult)
{
// We know that the IAsyncResult is really an
// AsyncResult<DateTime> object
AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;
// Wait for operation to complete, then return result or
// throw exception
return ar.EndInvoke();
}
}
测试代码
internal static class Test
{
private static int Main()
{
LongTask task = new LongTask(5);
task.BeginDoTask(ar=>{
task.EndDoTask((IAsyncResult)ar);
},task);
}
}
最后附上APM使用时的注意事项(多参考最佳实践,少犯低级错误)
1.APM的调用方式
a.BeginXxx还没结束,直接调用EndXxx
在一个线程操作完成之前调用EndXxx,并传递IAsyncResult对象,调用线程会因此阻塞并等待操作完成。
b.可以在BeginXxx返回的IAsyncResult对象的AsyncWaitHandle属性上调用WaitOne方法,从而是调用线程阻塞并等待异步操作结束。
以上两种调用方式,其实应该是不推荐大家使用的。因为他们会阻塞调用者线程,如果您正在使用线程池的话,线程池为了响应其他请求,会造成线程池再分配一个线程造成性能的损失。
c.你还可以在一个循环中连续的查询IAsyncResult对象的IsCompleted属性以发现操作在何时已完成。一般都会在循环中加入Sleep让线程休眠一段时间。不过这样做的不良后果就是我们既阻塞了线程,又在轮询--与其他计算限制的线程争抢CPU资源,让原本就慢的计算过程更耗时。
2.总是调用EndXxx方法,而且只调用一次
3.调用EndXxx方法时总是使用相同的对象