您好,欢迎访问一九零五行业门户网

C#异步之APM模式异步程序开发的示例分享

c#已有10多年历史,单从微软2年一版的更新进度来看活力异常旺盛,c#中的异步编程也经历了多个版本的演化,从今天起着手写一个系列博文,记录一下c#中的异步编程的发展历程。广告一下:喜欢我文章的朋友,请点下面的“关注我”。谢谢
我是2004年接触并使用c#的,那时c#版本为1.1,所以我们就从就那个时候谈起。那时在大学里自己看书写程序,所写的程序大都是同步程序,最多启动个线程........其实在c#1.1的时代已有完整的异步编程解决方案,那就是apm(异步编程模型)。如果还有不了解“同步程序、异步程序”的请自行百度哦。
apm异步编程模型最具代表性的特点是:一个异步功能由以begin开头、end开头的两个方法组成。begin开头的方法表示启动异步功能的执行,end开头的方法表示等待异步功能执行结束并返回执行结果。下面是一个模拟的实现方式(后面将编写标准的apm模型异步实现):
public class worker { public int a { get; set; } public int b { get; set; } private int r { get; set; } manualresetevent et; public void beginwork(action action) { et = new manualresetevent(false); new thread(() => { r = a + b; thread.sleep(1000); et.set(); if(null != action) { action(); } }).start(); } public int endwork() { if(null == et) { t hrow new exception("调用endwork前,需要先调用beginwork"); } else { et.waitone(); return r; } } }
static void main(string[] args) { worker w = new worker(); w.beginwork(()=> { console.writeline("thread id:{0},count:{1}", thread.currentthread.managedthreadid, w.endwork()); }); console.writeline("thread id:{0}", thread.currentthread.managedthreadid); console.readline(); }
在上面的模拟apm模型中我们使用了 thread、manualresetevent,如果你对多线程和manualresetevent不熟悉c#中使用异步编程不可避免的会涉及到多线程的知识,虽然微软在framework中做了很多封装,但朋友们应该掌握其本质。
上面模拟的apm异步模型之所以简单,是因为c#发展过程中引入了很多优秀的语法规则。上例我们较多的使用了lambda表达式,如果你不熟悉 匿名委托与lambda表达式可看我之前的bolg《匿名委托与lambda表达式》。上面做了如此多的广告,下面我们来看一下标准的apm模型如何实现异步编程。
iasyncresult接口
iasyncresult接口定义了异步功能的状态,该接口具体属性及含义如下:
// 表示异步操作的状态。 [comvisible(true)] public interface iasyncresult { // // 摘要: // 获取一个值,该值指示异步操作是否已完成。 // // 返回结果: // 如果操作已完成,则为 true;否则为 false。 bool iscompleted { get; } // // 摘要: // 获取用于等待异步操作完成的 system.threading.waithandle。 // // 返回结果: // 用于等待异步操作完成的 system.threading.waithandle。 waithandle asyncwaithandle { get; } // // 摘要: // 获取一个用户定义的对象,该对象限定或包含有关异步操作的信息。 // // 返回结果: // 一个用户定义的对象,限定或包含有关异步操作的信息。 object asyncstate { get; } // // 摘要: // 获取一个值,该值指示异步操作是否同步完成。 // // 返回结果: // 如果异步操作同步完成,则为 true;否则为 false。 bool completedsynchronously { get; } }
注意:模型示例1中的 manualresetevent 继承自 waithandle
apm传说实现方式
在了解了iasyncresult接口后,我们来通过实现 iasyncresult 接口的方式完成对模拟示例的改写工作,代码如下:
public class newworker { public class workerasyncresult : iasyncresult { asynccallback callback; public workerasyncresult(int a,int b, asynccallback callback, object asyncstate) { a = a; b = b; state = asyncstate; this.callback = callback; new thread(count).start(this); } public int a { get; set; } public int b { get; set; } public int r { get; private set; } private object state; public object asyncstate { get { return state; } } private manualresetevent waithandle; public waithandle asyncwaithandle { get { if (null == waithandle) { waithandle = new manualresetevent(false); } return waithandle; } } private bool completedsynchronously; public bool completedsynchronously { get { return completedsynchronously; } } private bool iscompleted; public bool iscompleted { get { return iscompleted; } } private static void count(object state) { var result = state as workerasyncresult; result.r = result.a + result.b; thread.sleep(1000); result.completedsynchronously = false; result.iscompleted = true; ((manualresetevent)result.asyncwaithandle).set(); if (result.callback != null) { result.callback(result); } } } public int num1 { get; set; } public int num2 { get; set; } public iasyncresult beginwork(asynccallback usercallback, object asyncstate) { iasyncresult result = new workerasyncresult(num1,num2,usercallback, asyncstate); return result; } public int endwork(iasyncresult result) { workerasyncresult r = result as workerasyncresult; r.asyncwaithandle.waitone(); return r.r; } }
示例代码分析:
上面代码中newworker的内部类 workerasyncresult 是关键点,它实现了 iasyncresult 接口并由它来负责开启新线程完成计算工作。
在workerasyncresult中增加了 a、b两个私有属性来存储用于计算的数值,一个对外可读不可写的属性r,用于存储workerasyncresult内部运算的结果。asyncwaithandle属性由 manualresetevent 来充当,并在首次访问时创建manualresetevent(但不释放)。其他接口属性正常实现,没有什么可说。
workerasyncresult 中新增 static count 方法,参数 state 为调用count方法的当前workerasyncresult对象。count 方法在 workerasyncresult 对象的新启线程中运行,因此thread.sleep(1000)将阻塞新线程1秒中。然后设置当前workerasyncresult对象是否同步完成为false,异步完成状态为true,释放manualresetevent通知以便等待线程获取通知进入执行状态,判断是否有异步执行结束回调委托,存在则回调之。
newworker 非常简单,num1、num2两个属性为要计算的数值。beginwork 创建workerasyncresult对象、并将要计算的两个数值num1、num2、usercallback回调委托、object 类型的 asyncstate 传入要创建的workerasyncresult对象。经过此步操作,workerasyncresult对象获取了运算所需的所有数据、运算完成后的回调,并马上启动新线程进行运算(执行workerasyncresult.count方法)。
因为workerasyncresult.count执行在新线程中,在该线程外部无法准确获知新线程的状态。为了满足外部线程与新线程同步的需求,在newworker中增加endwork方法,参数类型为iasyncresult。要调用endwork方法应传入beginwork 获取的workerasyncresult对象,endwork方法获取workerasyncresult对象后,调用workerasyncresult.asyncwaithandle.waitone()方法,等待获取manualresetevent通知,在获取到通知时运算线程已运算结束(线程并未结束),下一步获取运算结果r并返回。
接下来是newworker调用程序,如下:
static void main(string[] args) { newworker w2 = new newworker(); w2.num1 = 10; w2.num2 = 12; iasyncresult r = null; r = w2.beginwork((obj) => { console.writeline("thread id:{0},count:{1}",thread.currentthread.managedthreadid, w2.endwork(r)); }, null); console.writeline("thread id:{0}", thread.currentthread.managedthreadid); console.readline(); }
下图我简单画的程序调用过程,有助于各位朋友理解:
标准的apm模型异步编程,对应开发人员来说过于复杂。因此通过实现 iasyncresult 接口进行异步编程,就是传说中的中看不中用(罪过罪过.....)。
delegate异步编程(apm 标准实现)
c#中委托天生支持异步调用(apm模型),任何委托对象后"."就会发现begininvoke、endinvoke、invoke三个方法。begininvoke为异步方式调用委托、endinvoke等待委托的异步调用结束、invoke同步方式调用委托。因此上面的标准apm实例,可借助 delegate 进行如下简化。
上面newworker使用委托方式改写如下:

public class newworker2 { func<int, int, int> action; public newworker2() { action = new func<int, int, int>(work); } public iasyncresult beginwork(asynccallback callback, object state) { dynamic obj = state; return action.begininvoke(obj.a, obj.b, callback, this); } public int endwork(iasyncresult asyncresult) { try { return action.endinvoke(asyncresult); } catch (exception ex) { throw ex; } } private int work(int a, int b) { thread.sleep(1000); return a + b; } }
调用程序:
static void main(string[] args) { newworker2 w2 = new newworker2(); iasyncresult r = null; r = w2.beginwork((obj) => { console.writeline("thread id:{0},count:{1}", thread.currentthread.managedthreadid, w2.endwork(r)); }, new { a = 10, b = 11 }); console.writeline("thread id:{0}", thread.currentthread.managedthreadid); console.readline(); }
上面的使用委托进行apm异步编程,比实现 iasyncresult 接口的方式精简太多、更易理解使用。因此这里建议朋友们 delegate 异步调用模型应该掌握起来,而通过实现 iasyncresult 接口的传说方式看你的喜好吧。 
以上就是c#异步之apm模式异步程序开发的示例分享的详细内容。
其它类似信息

推荐信息