代码改变世界

异步编程模式学习

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入门新手,不求闻达,希望能为园子里乐于分享的精神能有所发扬,也希望能对园子里的新手们有所帮助,能得到牛人的指导。