经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C# » 查看文章
关于C#中async/await的用法
来源:cnblogs  作者:一叶、知秋  时间:2023/2/10 9:22:18  对本文有异议

一直对c#中async/await的用法模模糊糊,不太清晰,今天写了一下Demo彻底明确一下async/await的用法,以免因为对其不了解而对后期的业务产生影响(比如事务导致的锁表等等)。

1. 首先,async/await 成对出现才有意义。其意义在于可以等待异步操作完成后继续顺序执行,而不是异步操作还没处理完成主线程就进行了下一步。

    假设,我们现在要模拟简单的下载场景,首先用户点击下载,那么就调用DownloadHandle方法(异步)进行下载,然后通知用户下载完成。其使用 async/await  的区别如下:

    (1)使用 async/await 的情况:

  1. internal class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. DownloadHandle();
  6. Console.ReadLine();
  7. }
  8. /// <summary>
  9. /// 正常使用async/await时,符合正常的业务逻辑:
  10. /// 1. 通知用户下载开始
  11. /// 2. 异步下载
  12. /// 3. 等待异步下载完成后给用户提示下载完成
  13. /// </summary>
  14. public static async void DownloadHandle()
  15. {
  16. Console.WriteLine("下载开始!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);
  17. await Download();
  18. Console.WriteLine("下载完成!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);
  19. }
  20. /// <summary>
  21. /// 下载
  22. /// </summary>
  23. /// <returns></returns>
  24. public static Task Download()
  25. {
  26. return Task.Run(() =>
  27. {
  28. Console.WriteLine("下载线程ID:->" + Thread.CurrentThread.ManagedThreadId);
  29. Console.WriteLine("10%");
  30. Console.WriteLine("30%");
  31. Console.WriteLine("50%");
  32. Console.WriteLine("60%");
  33. Console.WriteLine("80%");
  34. Console.WriteLine("99%");
  35. Console.WriteLine("100%");
  36. });
  37. }
  38. }

结果如下:

 

 

 可以看到,即时下载使用了异步(线程ID不同也表明了当前使用了异步),业务逻辑最终还是按照我们的需求,按顺序正序执行了。

(2)不使用async/await的情况:

  1. internal class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. DownloadHandle();
  6. Console.ReadLine();
  7. }
  8. /// <summary>
  9. /// 不适用async/await时,则代码执行顺序时混乱的,不符合业务逻辑:
  10. /// 1. 通知用户下载开始
  11. /// 2. 提示下载完成
  12. /// 3. 开始下载
  13. /// </summary>
  14. public static void DownloadHandle()
  15. {
  16. Console.WriteLine("下载开始!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);
  17. Download();
  18. Console.WriteLine("下载完成!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);
  19. }
  20. /// <summary>
  21. /// 下载
  22. /// </summary>
  23. /// <returns></returns>
  24. public static Task Download()
  25. {
  26. return Task.Run(() =>
  27. {
  28. Console.WriteLine("下载线程ID:->" + Thread.CurrentThread.ManagedThreadId);
  29. Console.WriteLine("10%");
  30. Console.WriteLine("30%");
  31. Console.WriteLine("50%");
  32. Console.WriteLine("60%");
  33. Console.WriteLine("80%");
  34. Console.WriteLine("99%");
  35. Console.WriteLine("100%");
  36. });
  37. }
  38. }

结果如下:

 

 

 可以看到,代码执行顺序混乱了,“下载完成” 跑到了 “下载线程ID” 前面去了,完全没有按照我们预期的顺序执行。

2. 如果可以await的方法不进行await,那将会怎样呢?

(1)如果被调用的异步方法内部使用了Task.Run,那结果可参考我们1中进行讲述的结果。开发者可根据实际需要来进行调用,如果异步方法的调用结果与其上下文逻辑没有严格的执行要求,则可以不进行await(比如记录日志等等)。反之,则需要加await。

(2)如果被调用的异步方法内部只是返回了Task.CompletedTask,即时使用了await/async实际上还是等于同步执行,如下图。

  1. internal class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. DownloadHandle();
  6. Console.ReadLine();
  7. }
  8. /// <summary>
  9. /// 模拟下载
  10. /// </summary>
  11. public static async void DownloadHandle()
  12. {
  13. Console.WriteLine("下载开始!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);
  14. await Download();
  15. Console.WriteLine("下载完成!->主线程ID:" + Thread.CurrentThread.ManagedThreadId);
  16. }
  17. /// <summary>
  18. /// 下载
  19. /// </summary>
  20. /// <returns></returns>
  21. public static Task Download()
  22. {
  23. Console.WriteLine("下载线程ID:->" + Thread.CurrentThread.ManagedThreadId);
  24. Console.WriteLine("10%");
  25. Console.WriteLine("30%");
  26. Console.WriteLine("50%");
  27. Console.WriteLine("60%");
  28. Console.WriteLine("80%");
  29. Console.WriteLine("99%");
  30. Console.WriteLine("100%");
  31. return Task.CompletedTask;
  32. }

结果如图:

 

 

 可以看到,即使DonwloadHandle方法使用了await/async,还是进行了同步执行,并没有异步效果(可从所有线程ID相同看出)

3. 小技巧: 异步方法的返回值类型一般都是Task或者Task<T>类型的,当返回值为Task时(即方法的返回值类型为void),我们可以直接return Task.Run(()=>{})(以下第一段代码),而不必await Task.Run(()=>{})(以下第二段代码),这样也可从一定程度上提高代码执行效率。另外,不推荐使用async 修饰void返回值,会有异常处理方面的问题(非常感谢 残生 指教)。

  1. /// <summary>
  2. /// 下载
  3. /// </summary>
  4. /// <returns></returns>
  5. public static Task Download()
  6. {
  7. return Task.Run(() =>
  8. {
  9. Console.WriteLine("下载线程ID:->" + Thread.CurrentThread.ManagedThreadId);
  10. Console.WriteLine("10%");
  11. Console.WriteLine("30%");
  12. Console.WriteLine("50%");
  13. Console.WriteLine("60%");
  14. Console.WriteLine("80%");
  15. Console.WriteLine("99%");
  16. Console.WriteLine("100%");
  17. });
  18. }
  1. /// <summary>
  2. /// 下载
  3. /// </summary>
  4. /// <returns></returns>
  5. public static async Task Download()
  6. {
  7. await Task.Run(() =>
  8. {
  9. Console.WriteLine("下载线程ID:->" + Thread.CurrentThread.ManagedThreadId);
  10. Console.WriteLine("10%");
  11. Console.WriteLine("30%");
  12. Console.WriteLine("50%");
  13. Console.WriteLine("60%");
  14. Console.WriteLine("80%");
  15. Console.WriteLine("99%");
  16. Console.WriteLine("100%");
  17. });
  18. }

 

以上,只是作者的个人理解,请大神勿喷,如有错误,欢迎指正,谢谢!

原文链接:https://www.cnblogs.com/w821759016/p/17101852.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号