• 对[yield]的浅究到发现[async][await]


      上篇对[foreach]的浅究到发现[yield]写完后,觉得对[yield]还没有理解清楚,想起曾经看过一位大牛的帖子讲的很深刻(链接在此),回顾了下,在这里写出自己的理解,与各位分享。

    一、通常的异步

      现在我们假设一种平时经常遇到的情况,现有三个方法,其中funcOne和funcTwo比较耗时需要异步执行,而且他们的逻辑是必须在funcOne执行完后才可以执行funcTwo,同理funcTwo执行完后才能执行funcThree。

      按照这样的设定,通常的做法请看代码段[1]:

     1      public class Program
     2      {
     3          public delegate void CallBack(string nextName);
     4          public void funcOne(CallBack callback)
     5          {
     6              ThreadPool.QueueUserWorkItem((state1) =>
     7              {
     8                  Console.WriteLine("[One] async Continue!");
     9                  Console.WriteLine("[One] do something!");
    10                  callback("Called Two");
    11              });
    12          }
    13          public void funcTwo(CallBack callback)
    14          {
    15              ThreadPool.QueueUserWorkItem((state2) =>
    16                     {
    17                         Console.WriteLine("[Two] async Continue!");
    18                         Console.WriteLine("[Two] do something!");
    19                         callback("Called Three");
    20                     });
    21          }
    22          public void funcThree(CallBack callback)
    23          {
    24              Console.WriteLine("[Three] do something!");
    25              callback("Called ...");
    26          }
    27          static void Main()
    28          {
    29              Program p = new Program();
    30              p.funcOne((name1) =>
    31              {
    32                  Console.WriteLine(name1);
    33                  p.funcTwo((name2) =>
    34                  {
    35                      Console.WriteLine(name2);
    36                      //执行funcThree
    37                      p.funcThree((name3) =>
    38                      {
    39                          Console.WriteLine(name3);
    40                          //当然还有可能继续嵌套
    41                          Console.WriteLine("End!");
    42                      });
    43                  });
    44              });
    45              Console.Read();
    46          }
    47      }
    异步的通常实现

      相信看完代码后我们的感觉是一样的,好繁琐,就是不断的嵌套!那有没有方法可以避免这样呢,也就是说用同步的写法来写异步程序。

    二、改进后的异步

      该[yield]粉墨登场了,先看代码段[2]:

     1         //三个方法以及委托CallBack的定义不变,此处不再列出。
     2         //新增了静态的全局变量enumerator,和静态方法funcList.
     3         public static System.Collections.IEnumerator enumerator = funcList();
     4         public static System.Collections.IEnumerator funcList()
     5         {
     6             Program p = new Program();
     7             p.funcOne((name1) =>
     8             {
     9                 enumerator.MoveNext();
    10             });
    11             yield return 1;
    12             Console.WriteLine("Called Two");
    13             p.funcTwo((name2) =>
    14             {
    15                 enumerator.MoveNext();
    16             });
    17             yield return 2;
    18             Console.WriteLine("Called Three");
    19             p.funcThree((name3) =>
    20             {
    21                 //当然还有可能继续嵌套
    22             });
    23             Console.WriteLine("Called ...");
    24             Console.WriteLine("End!");
    25             yield return 3;
    26         }
    27 
    28       //变化后的Main函数
    29       static void Main()
    30         {
    31             enumerator.MoveNext();
    32             Console.Read();
    33         }    
    改进后的异步

      现在看看,是不是清爽了一些,没有无止尽的嵌套。代码中我们只需要定义一个迭代器,在迭代器中调用需要同步执行的方法,用[yield]来分隔,各方法通过在callback里调用迭代器的MoveNext()方法来保持同步。

      通过这样的实践,我们可以理解为每当[yield return ..],程序会把迭代器的[上下文环境]暂时保存下来,等到MoveNext()时,再调出来继续执行到下一个[yield return ..]。

    三、是我想太多

      以上纯属瞎扯,在.net 4.5之后,我们有了神器:async/await,下面看看多么简洁吧。

      代码段[3]:

     1      public class Program
     2     {
     3         public async Task funcOne()
     4         {
     5             Console.WriteLine("[One] async Continue!");
     6             Console.WriteLine("[One] do something!");
     7             //await ...
     8         }
     9         public async Task funcTwo()
    10         {
    11             Console.WriteLine("[Two] async Continue!");
    12             Console.WriteLine("[Two] do something!");
    13             //await ...
    14         }
    15         public void funcThree()
    16         {
    17             Console.WriteLine("[Three] do something!");
    18         }
    19         public static async Task funcList()
    20         {
    21             Program p = new Program();
    22             await p.funcOne();
    23             await p.funcTwo();
    24             p.funcThree();
    25             //无尽的嵌套可以继续await下去。
    26             Console.WriteLine("End!");
    27         }
    28         static void Main()
    29         {
    30             funcList();
    31             Console.Read();
    32         }
    33     }
    async/await

      我已经感觉到了您的惊叹之情。

      async修饰符将方法指定为异步方法(注意!:异步不异步,并不是async说了算,如果这个方法中没有await语句,就算用了async修饰符,它依然是同步执行,因为它就没有异步基因)。

      被指定为异步方法后,方法的返回值只能为Task、Task<TResult>或者void,返回的Task对象用来表示这个异步方法,你可以对这个Task对象进行控制来操作这个异步方法,详细的可以参考msdn中给出的Task解释与示例代码

      await用于挂起主线程(这里的主线程是相对的),等待这个异步方法执行完成并返回,接着才继续执行主线程的方法。用了await,主线程才能得到异步方法返回的Task对象,以便于在主线程中对它进行操作。如果一个异步方法调用时没有用await,那么它就会去异步执行,主线程不会等待,就像我们代码段[3]中Main方法里对funcList方法的调用那样。

      最后就分析到这儿吧,希望各位兄弟博友能一起讨论,共同进步。

      当然,别吝啬您的[赞]哦 :)

     更新:现在大家看到的是改进之后的文章,在此谢谢文章下面几位高手的讨论,让我学会了不少。抱拳!

      

  • 相关阅读:
    一个十分诡异的NullReferenceException异常!
    如何去掉TabControl控件默认添加的TabPage
    GDI+发生一般性错误的解决方法
    C#中各种数组的性能比较
    酷享娱乐新生活
    关于ImageList.Images集合的特殊行为!
    WinForm窗体之间交互的一些方法
    实现单实例应用程序的三种方案
    MySql_Close 释放资源
    数组之List
  • 原文地址:https://www.cnblogs.com/such/p/3783104.html
Copyright © 2020-2023  润新知