异步编程模式学习
2012-08-20 18:43 Johnnie Zhang 阅读(717) 评论(2) 编辑 收藏 举报最近,在学习C#多线程编程,也看了园子里的很多大牛的关于多线程的文章,梳理下自己的思路,也总结下异步编程模式的学习。
很喜欢Jimmy Zhang的文章风格,在刚刚学习委托和事件的时候,Jimmy的文章的由浅入深的写作对我对委托和事件的学习的帮助很大。我也学这个Jimmy的文风,和大家交流下。废话不多说的,通过一个抛12点的小游戏的例子进入正题。
没有异步模式的多线程实现
游戏的规则:游戏随机产生的0到12之间的数字,碰到数字12游戏结束。实现如下:例1
static void Main(string[] args) { Thread thread = new Thread(new ThreadStart(ThreadFun)); thread.Start(); Console.WriteLine("Game is starting..."); thread.Join(); Console.WriteLine("Number 12 is createed.Game is over..."); Console.ReadKey(); } static void ThreadFun() { Random rnd = new Random(); int rndNum; do { rndNum = rnd.Next(0, 13); Console.WriteLine(rndNum); } while (rndNum != 12); }
异步编程模式的实现
这个小游戏的异步模式编程实现如下代码:例2
delegate void asycInvoker(); static void Main(string[] args) { asycInvoker asycHander = ThreadFun;//此处是asycInvoker asycHander = new asycInvoker(ThreadFun)的快捷语法 IAsyncResult result = asycHander.BeginInvoke(null,null); Console.WriteLine("Game is starting..."); asycHander.EndInvoke(result); Console.WriteLine("Number 12 is createed.Game is over..."); Console.ReadKey(); } static void ThreadFun() { Random rnd = new Random(); int rndNum; do { rndNum = rnd.Next(0, 13); Console.WriteLine(rndNum); } while (rndNum != 12); }
异步编程模式和多线程的关系
异步编程模式技术为多线程处理技术的强大扩展方式。C#的异步模式的内部的实现是通过线程池的挂起和释放实现一步操作。C#的异步模式的好处降低创建和销毁线程相关的成本,CLR为每一个进程维护一个线程池。
由此,我们必须清楚线程池的优缺点的。线程池的优点是很明显的,缺点是一旦加入线程池,就没办法控制程序的执行,程序的执行都在线程池的自动管理。
.NET的异步编程模式
Net 在设计的时候为异步编程设计了一个异步编程模型(APM),这个模型不仅是使用.NET的开发人员使用,.Net内部也频繁用到。通过使用委托,我们可以异步实现委托列表上的方法(委托列表只能有一个方法)。委托类有两个方法:叫做BeginInvoke和EndInvoke,他们就是用来实现异步操作过程。
当我们调用委托的BeginInvoke方法时,它开始在线程池中的独立线程上执行引用方法,并且立即返回原始线程。原始线程可以继续,而引用方法会在线程池的线程中执行。
当程序异步方法执行的时候,主线程和异步方法的线程池线程之间的同步过程不同处理,就会有不同的实现模式,当异步方法执行结束,可以检查BeginInvoke返回的IAsyncResult的IsCompleted属性,或者调用委托的EndInvoke方法等待委托的完成,或着在委托的回调方法中调用EndInvoke方法完成委托。
上述过程有异步编程的3种标准模式的过程。这三种过程,原始线程都发起了一个异步方法,这些模式不同的是,原始线程获得发起的线程已经完成的消息的方式。标准的三种模式有等待完成模式、轮询模式和回调模式。通过代码将每一种模式表述下:
等待-结束模式
在这个模式中,原始线程发起异步模式方法的调用,原始线程和异步方法所在的线程池线程并发(可能这里用并发描述不是很恰当)运行,如果原始线程一直到调用委托方法的EndInvoke时,委托方法还没有执行完,原始线程就等待异步线程结束,如果有返回值的话,返回异步方法的返回值。代码如例1所示。
AsyncResult类
在BeginInvoke方法的调用的时候,返回一个IAsyncResult接口的引用,在内部实现AsyncResult类的对象。AsyncResult类表现了异步方法的状态。有关这个类的需要说明的如下:
- 我们调用委托对象的BeginInvoke方法时,系统创建了一个AsyncResult类的对象。BeingInvoke方法返回对象实现的接口的引用。
- AsyncResult对象包含一个叫做AsyncDelegate的属性,该属性返回指向被调用的产生异步方法的委托对象的引用。
- IsCompleted属性表示异步方法是否完成。
- AsyncState属性作为BeginInvoke方法调用时的state参数,他返回object类型的引用。
轮询模式
在轮询模式中,原始线程发起的异步方法的调用,原始线程就继续执行,然后用IasyncResult的IsComplete属性来定期检查异步线程是否完成。如果异步方法已经完成,原始线程就调用EndInvoke并继续,否则,继续检查执行。代码实例3
delegate void asycInvoker(); static void Main(string[] args) { asycInvoker asycHander = new asycInvoker(ThreadFun); IAsyncResult result = asycHander.BeginInvoke(null,null); Console.WriteLine("Game is starting..."); while (!result.IsCompleted) { Console.WriteLine("Game is not completed..."); //此处可以继续执行其他的逻辑 } asycHander.EndInvoke(result); Console.WriteLine("Number 12 is createed.Game is over..."); Console.ReadKey(); } static void ThreadFun() { Thread.Sleep(100);//暂时挂起该线程,放大整个执行的过程 Random rnd = new Random(); int rndNum; do { rndNum = rnd.Next(0, 13); Console.WriteLine(rndNum); } while (rndNum != 12); }
回调模式
在前面的模式中,等待-结束和轮询模式中,初始线程都是继续自己的执行过程,等待异步的线程完成。然后,在EndInvoke获得异步方法的返回值,并初始线程继续后面的逻辑执行。在效率地上,轮询通常不是最好的方法。所以,在异步编程中使用通知机制,能在一个异步方法完成时触发一个方法。
我们回到BeginInvoke方法的参数来谈的,BeginInvoke的参数根据定义的委托类型不断变化的,但是最后两个参数是不会改变的,AsyncCllback委托类型的原型为:delegate void AsyncCallback(IAsyncResult result),最后一个参数为object类型,稍后会谈到object类型解决的问题。回调的方法的通知机制关键就在AsyncCllback委托类型参数的使用,在AsyncCallback委托的实例中调用EndInvoke方法,返回异步方法的返回值,而AsyncCallback实例在异步方法一结束就调用。通过代码演练实例4:
delegate void asycInvoker(); static void Main(string[] args) { asycInvoker asycHander = new asycInvoker(ThreadFun); IAsyncResult result = asycHander.BeginInvoke(new AsyncCallback(CallbackFun),null); Console.WriteLine("Game is starting..."); Console.ReadKey(); } static void CallbackFun(IAsyncResult result) { AsyncResult asyncResult = (AsyncResult)result; asycInvoker handler = (asycInvoker)asyncResult.AsyncDelegate; handler.EndInvoke(result); Console.WriteLine("Number 12 is createed.Game is over..."); } static void ThreadFun() { Console.WriteLine("asyncMethod is runing..."); Random rnd = new Random(); int rndNum; do { rndNum = rnd.Next(0, 13); Console.WriteLine(rndNum); } while (rndNum != 12); }
回调过程传递参数
为能具体的展示object类型参数的应用,这次将这个小程序的要求改变一下,我们可以随机选择结束的数字,游戏在产生我们需要的数字时结束。通过代码的演示例5
delegate void asycInvoker(int endNum); static void Main(string[] args) { Console.WriteLine("Select the End Number:"); int endNum = int.Parse(Console.ReadLine()); asycInvoker asycHander = new asycInvoker(ThreadFun); IAsyncResult result = asycHander.BeginInvoke(endNum,new AsyncCallback(CallbackFun),endNum); Console.WriteLine("Game is starting..."); Console.ReadKey(); } static void CallbackFun(IAsyncResult result) { int endNum = (int)result.AsyncState; AsyncResult asyncResult = (AsyncResult)result; asycInvoker handler = (asycInvoker)asyncResult.AsyncDelegate; handler.EndInvoke(result); Console.WriteLine("Number {0} is createed.Game is over...",endNum);//此处为传递的参数的用途 } static void ThreadFun(int endNum) { Console.WriteLine("asyncMethod is runing..."); Random rnd = new Random(); int rndNum; do { rndNum = rnd.Next(0, endNum); Console.WriteLine(rndNum); } while (rndNum != 12); }
关于异步编程的使用,推荐老赵的博客写到的正确使用异步操作。
以上是我对学习异步编程模式的知识的梳理,在大神云集的园子里,作为.NET入门新手,不求闻达,希望能为园子里乐于分享的精神能有所发扬,也希望能对园子里的新手们有所帮助,能得到牛人的指导。