C# 中的异步编程

一,编写的目的

主要是为了介绍C#中异步编程相关的概念与本人使用的心得总结,对阅读与参考的网络资料进行了整理与归类,记录与明确本人在日常代码中关于异步编程的一些编写习惯与规范。

二、预期读者

熟悉.NET编程,具有.NET代码的阅读与编写基础。

三、异步编程的一些基本概念

四、C# 中的异步编程例子 

  • Task的使用

    1、C# Task详解

    2、C# Task和async/await详解

    3、Task调度器

    4、优雅地使用C#异步

    5、C#同步方法中如何调用异步方法

  • Task.Run和Task.Factory.StartNew

    1、C# Task.Run 和 Task.Factory.StartNew 区别

    2、Task.Run Vs Task.Factory.StartNew

  • 使用 Async 和 Await 的异步编程

    1、async和await详解

    2、核心代码

      1     public class Breakfast
      2     {
      3         /// <summary>
      4         /// 同步版本
      5         /// </summary>
      6         [Benchmark(Baseline = true)]
      7         public void RunV1()
      8         {
      9             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 开始....");
     10             var st = Stopwatch.StartNew();
     11 
     12             Coffee cup = PourCoffee();
     13             PourCoffeeSuccess();
     14 
     15             Egg eggs = FryEggs(2);
     16             FryEggsSuccess();
     17 
     18             Bacon bacon = FryBacon(3);
     19             FryBaconSuccess();
     20 
     21             Toast toast = ToastBread(2);
     22             ApplyButter(toast);
     23             ApplyJam(toast);
     24             ToastBreadSuccess();
     25 
     26             Juice oj = PourOJ();
     27             PourOJSuccess();
     28             Console.WriteLine($"Breakfast is ready!{Environment.NewLine}");
     29             st.Stop();
     30             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 结束,耗时 = {st.ElapsedMilliseconds.ToString("###,###")} 毫秒");
     31         }
     32 
     33         /// <summary>
     34         /// 异步版本,注意 await 关键字
     35         /// </summary>
     36         /// <returns></returns>
     37         [Benchmark]
     38         public async Task RunV2()
     39         {
     40             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 开始....");
     41             var st = Stopwatch.StartNew();
     42 
     43             Coffee cup = PourCoffee();
     44             PourCoffeeSuccess();
     45 
     46             // 异步煎鸡蛋
     47             Egg eggs = await FryEggsAsync(2);
     48             FryEggsSuccess();
     49 
     50             // 异步煎培根
     51             Bacon bacon = await FryBaconAsync(3);
     52             FryBaconSuccess();
     53 
     54             // 异步烤面包
     55             Toast toast = await ToastBreadAsync(2);
     56             ApplyButter(toast);
     57             ApplyJam(toast);
     58             ToastBreadSuccess();
     59 
     60             Juice oj = PourOJ();
     61             PourOJSuccess();
     62 
     63             Console.WriteLine($"Breakfast is ready!{Environment.NewLine}");
     64             st.Stop();
     65             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 结束,耗时 = {st.ElapsedMilliseconds.ToString("###,###")} 毫秒");
     66         }
     67 
     68         [Benchmark]
     69         public async Task RunV3()
     70         {
     71             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 开始....");
     72             var st = Stopwatch.StartNew();
     73 
     74             Coffee cup = PourCoffee();
     75             PourCoffeeSuccess();
     76 
     77             var eggsTask = FryEggsAsync(2);
     78             await eggsTask;
     79             FryEggsSuccess();
     80 
     81             var baconTask = FryBaconAsync(3);
     82             await baconTask;
     83             FryBaconSuccess();
     84 
     85             var toastTask = ToastBreadAsync(2);
     86             var toast = await toastTask;
     87             ApplyButter(toast);
     88             ApplyJam(toast);
     89             ToastBreadSuccess();
     90 
     91             Juice oj = PourOJ();
     92             PourOJSuccess();
     93 
     94             Console.WriteLine($"Breakfast is ready!{Environment.NewLine}");
     95             st.Stop();
     96             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 结束,耗时 = {st.ElapsedMilliseconds.ToString("###,###")} 毫秒");
     97         }
     98 
     99         [Benchmark]
    100         public async Task RunV4()
    101         {
    102             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 开始....");
    103             var st = Stopwatch.StartNew();
    104 
    105             Coffee cup = PourCoffee();
    106             PourCoffeeSuccess();
    107 
    108             var eggsTask = FryEggsAsync(2);
    109             var baconTask = FryBaconAsync(3);
    110             var toastTask = ToastBreadAsync(2);
    111 
    112             await eggsTask;
    113             FryEggsSuccess();
    114 
    115             await baconTask;
    116             FryBaconSuccess();
    117 
    118             var toast = await toastTask;
    119             ApplyButter(toast);
    120             ApplyJam(toast);
    121             ToastBreadSuccess();
    122 
    123             Juice oj = PourOJ();
    124             PourOJSuccess();
    125 
    126             Console.WriteLine($"Breakfast is ready!{Environment.NewLine}");
    127             st.Stop();
    128             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 结束,耗时 = {st.ElapsedMilliseconds.ToString("###,###")} 毫秒");
    129         }
    130 
    131         [Benchmark]
    132         public async Task RunV5()
    133         {
    134             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 开始....");
    135             var st = Stopwatch.StartNew();
    136 
    137             Coffee cup = PourCoffee();
    138             PourCoffeeSuccess();
    139 
    140             var eggsTask = FryEggsAsync(2);
    141             var baconTask = FryBaconAsync(3);
    142             var toastTask = MakeToastWithButterAndJamAsync(2);
    143 
    144             await eggsTask;
    145             FryEggsSuccess();
    146 
    147             await baconTask;
    148             FryBaconSuccess();
    149 
    150             var toast = await toastTask;
    151             ToastBreadSuccess();
    152 
    153             Juice oj = PourOJ();
    154             PourOJSuccess();
    155 
    156             Console.WriteLine($"Breakfast is ready!{Environment.NewLine}");
    157             st.Stop();
    158             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 结束,耗时 = {st.ElapsedMilliseconds.ToString("###,###")} 毫秒");
    159         }
    160 
    161         [Benchmark]
    162         public async Task RunV6()
    163         {
    164             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 开始....");
    165             var st = Stopwatch.StartNew();
    166 
    167             Coffee cup = PourCoffee();
    168             PourCoffeeSuccess();
    169 
    170             var eggsTask = FryEggsAsync(2);
    171             var baconTask = FryBaconAsync(3);
    172             var toastTask = MakeToastWithButterAndJamAsync(2);
    173 
    174             await Task.WhenAll(eggsTask, baconTask, toastTask);
    175             FryEggsSuccess();
    176             FryBaconSuccess();
    177             ToastBreadSuccess();
    178 
    179             Juice oj = PourOJ();
    180             PourOJSuccess();
    181 
    182             Console.WriteLine($"Breakfast is ready!{Environment.NewLine}");
    183             st.Stop();
    184             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 结束,耗时 = {st.ElapsedMilliseconds.ToString("###,###")} 毫秒");
    185         }
    186 
    187         [Benchmark]
    188         public async Task RunV7()
    189         {
    190             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 开始....");
    191             var st = Stopwatch.StartNew();
    192 
    193             Coffee cup = PourCoffee();
    194             PourCoffeeSuccess();
    195 
    196             var eggsTask = FryEggsAsync(2);
    197             var baconTask = FryBaconAsync(3);
    198             var toastTask = MakeToastWithButterAndJamAsync(2);
    199             var breakfastTasks = new List<Task>() { eggsTask, baconTask, toastTask };
    200             while (breakfastTasks.Count > 0)
    201             {
    202                 var task = await Task.WhenAny(breakfastTasks);
    203                 if (task == eggsTask)
    204                 {
    205                     FryEggsSuccess();
    206                 }
    207                 else if (task == baconTask)
    208                 {
    209                     FryBaconSuccess();
    210                 }
    211                 else if (task == toastTask)
    212                 {
    213                     ToastBreadSuccess();
    214                 }
    215 
    216                 breakfastTasks.Remove(task);
    217             }
    218 
    219             Juice oj = PourOJ();
    220             PourOJSuccess();
    221 
    222             Console.WriteLine($"Breakfast is ready!{Environment.NewLine}");
    223             st.Stop();
    224             Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 结束,耗时 = {st.ElapsedMilliseconds.ToString("###,###")} 毫秒");
    225         }
    226 
    227 
    228         /// <summary>
    229         /// 倒一杯橙汁
    230         /// </summary>
    231         /// <returns></returns>
    232         private static Juice PourOJ()
    233         {
    234             Console.WriteLine("Pouring orange juice(倒一杯橙汁)");
    235             Thread.Sleep(100);
    236             return new Juice();
    237         }
    238         /// <summary>
    239         /// 面包加果酱
    240         /// </summary>
    241         /// <param name="toast"></param>
    242         private static void ApplyJam(Toast toast)
    243         {
    244             Thread.Sleep(50);
    245             Console.WriteLine("Putting jam on the toast(面包加果酱)");
    246         }
    247         /// <summary>
    248         /// 面包加黄油
    249         /// </summary>
    250         /// <param name="toast"></param>
    251         private static void ApplyButter(Toast toast)
    252         {
    253             Thread.Sleep(50);
    254             Console.WriteLine("Putting butter on the toast(面包加黄油)");
    255         }
    256         /// <summary>
    257         /// 烤面包
    258         /// </summary>
    259         /// <param name="slices"></param>
    260         /// <returns></returns>
    261         private static Toast ToastBread(int slices)
    262         {
    263             Console.WriteLine("Start toasting(烤面包)...");
    264             for (int slice = 0; slice < slices; slice++)
    265             {
    266                 Console.WriteLine("Putting a slice of bread in the toaster");
    267                 Thread.Sleep(100);
    268             }
    269             Console.WriteLine("Remove toast from toaster");
    270 
    271             return new Toast();
    272         }
    273         /// <summary>
    274         /// 煎培根
    275         /// </summary>
    276         /// <param name="slices"></param>
    277         /// <returns></returns>
    278         private static Bacon FryBacon(int slices)
    279         {
    280             Console.WriteLine($"putting {slices} slices of bacon in the pan(煎培根)");
    281             Console.WriteLine("cooking first side of bacon...");
    282             for (int slice = 0; slice < slices; slice++)
    283             {
    284                 Console.WriteLine("flipping a slice of bacon");
    285                 Thread.Sleep(100);
    286             }
    287             Console.WriteLine("cooking the second side of bacon...");
    288             Console.WriteLine("Put bacon on plate");
    289 
    290             return new Bacon();
    291         }
    292         /// <summary>
    293         /// 煎鸡蛋
    294         /// </summary>
    295         /// <param name="howMany"></param>
    296         /// <returns></returns>
    297         private static Egg FryEggs(int howMany)
    298         {
    299             Console.WriteLine("Warming the egg pan(煎鸡蛋)...");
    300             Console.WriteLine($"cracking {howMany} eggs");
    301             Console.WriteLine("cooking the eggs ...");
    302             for (int i = 0; i < howMany; i++)
    303             {
    304                 Thread.Sleep(100);
    305             }
    306             Console.WriteLine("Put eggs on plate");
    307 
    308             return new Egg();
    309         }
    310         /// <summary>
    311         /// 倒咖啡
    312         /// </summary>
    313         /// <returns></returns>
    314         private static Coffee PourCoffee()
    315         {
    316             Console.WriteLine("Pouring coffee(倒咖啡)");
    317             Thread.Sleep(100);
    318             return new Coffee();
    319         }
    320 
    321         private static void PourOJSuccess()
    322         {
    323             Console.WriteLine("oj is ready(橙汁已倒好)");
    324         }
    325         private static void ToastBreadSuccess()
    326         {
    327             Console.WriteLine($"toast is ready(面包已烤好){Environment.NewLine}");
    328         }
    329         private static void FryBaconSuccess()
    330         {
    331             Console.WriteLine($"bacon is ready({3}片培根已煎好){Environment.NewLine}");
    332         }
    333         private static void FryEggsSuccess()
    334         {
    335             Console.WriteLine($"eggs are ready({2}个鸡蛋已煎好){Environment.NewLine}");
    336         }
    337         private static void PourCoffeeSuccess()
    338         {
    339             Console.WriteLine($"coffee is ready(咖啡已倒好){Environment.NewLine}");
    340         }
    341 
    342 
    343         /// <summary>
    344         /// 煎鸡蛋
    345         /// </summary>
    346         /// <param name="howMany"></param>
    347         /// <returns></returns>
    348         private static async Task<Egg> FryEggsAsync(int howMany)
    349         {
    350             Console.WriteLine("Warming the egg pan(煎鸡蛋)...");
    351             Console.WriteLine($"cracking {howMany} eggs");
    352             Console.WriteLine("cooking the eggs ...");
    353             for (int i = 0; i < howMany; i++)
    354             {
    355                 await Task.Delay(100);
    356             }
    357             Console.WriteLine("Put eggs on plate");
    358 
    359             return new Egg();
    360         }
    361         /// <summary>
    362         /// 煎培根
    363         /// </summary>
    364         /// <param name="slices"></param>
    365         /// <returns></returns>
    366         private static async Task<Bacon> FryBaconAsync(int slices)
    367         {
    368             Console.WriteLine($"putting {slices} slices of bacon in the pan(煎培根)");
    369             Console.WriteLine("cooking first side of bacon...");
    370             for (int slice = 0; slice < slices; slice++)
    371             {
    372                 Console.WriteLine("flipping a slice of bacon");
    373                 await Task.Delay(100);
    374             }
    375             Console.WriteLine("cooking the second side of bacon...");
    376             Console.WriteLine("Put bacon on plate");
    377 
    378             return new Bacon();
    379         }
    380         /// <summary>
    381         /// 烤面包
    382         /// </summary>
    383         /// <param name="slices"></param>
    384         /// <returns></returns>
    385         private static async Task<Toast> ToastBreadAsync(int slices)
    386         {
    387             Console.WriteLine("Start toasting(烤面包)...");
    388             for (int slice = 0; slice < slices; slice++)
    389             {
    390                 Console.WriteLine("Putting a slice of bread in the toaster");
    391                 await Task.Delay(100);
    392             }
    393             //Console.WriteLine("Fire! Toast is ruined!");
    394             //throw new InvalidOperationException("The toaster is on fire");
    395             Console.WriteLine("Remove toast from toaster");
    396 
    397             return new Toast();
    398         }
    399         /// <summary>
    400         /// 烤面包 + 黄油 + 果酱
    401         /// </summary>
    402         /// <param name="number"></param>
    403         /// <returns></returns>
    404         private static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
    405         {
    406             var toast = await ToastBreadAsync(number);
    407             ApplyButter(toast);
    408             ApplyJam(toast);
    409 
    410             return toast;
    411         }
    412     }
    点击查看代码

    代码下载,点击此处

五、工作机制与原理

六、总结

  • 推荐做法

  1. 在.NET中,推荐使用基于任务的异步模式。它基于 System.Threading.Tasks 命名空间中的 Task  Task<TResult> 类型,这些类型用于表示异步操作。

  2. 避免 Async Void 最好使用 async Task 方法而不是 async void 方法。

  3. 始终使用 Async 不要混合阻塞式代码和异步代码。

  4. 配置上下文尽可能使用 ConfigureAwait(false)。

Async/Await 异步编程中的最佳做法 | Microsoft Docs

  • 使用过程中可能出现的误区

  1. Task.Factory.StartNew(async () =>{});的写法

如下面的错误写法 

  var task= Task.Factory.StartNew(async () =>
  {
      // task job.
      await Task.Delay(1000);
  });
  await task;

正确写法

方式一(推荐):

  var task= Task.Run(async () =>
  {
      // task job.
      await Task.Delay(1000);
  });
  await task;

方式二:

  var task= Task.Factory.StartNew(async () =>
  {
      // task job.
      await Task.Delay(1000);
  }).Unwrap();
  await task;

方式三:

  var task= await Task.Factory.StartNew(async () =>
  {
      // task job.
      await Task.Delay(1000);
  });
  await task;

 相关文章:Task.Run(), Task.Factory.StartNew() 和 New Task() 的行为不一致分析 

七、特别声明

  • 本文引用了其他博友写的文章,如果有涉及到侵权问题请及时联系我进行删除。

  • 本文涉及到的一些文章未经过严格的推敲于论证,如果大家有发现问题欢迎大家指出,然后我再进行更改。

  • 涉及到这个模块好的文章也欢迎大家进行推荐或自荐,本人阅读论证后会进行引用添加。

posted @ 2022-07-02 15:32  lanwah  阅读(781)  评论(0编辑  收藏  举报