• c# async Task await Result 死锁


    最近项目数据量较大,使用 async Task异步增加执行效率

    遇到问题,当前有2个计算非常耗时,现在需要你优化一下,这2个计算并行执行,2个计算执行完成后将2个结果sum返回给用户

    当前我是这样实现的

     1 public async Task<ActionResult> Index()
     2         {
     3             System.Diagnostics.Debug.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}begin");
     4            
     5             try
     6             {
     7                 var t1Rlt =  Test1();
     8                 var t2Rlt =  Test2();
     9                 var a =  t1Rlt.Result;
    10                 var b =  t2Rlt.Result;
    11                 System.Diagnostics.Debug.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}结果【{a + b}】");
    12                 System.Diagnostics.Debug.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}end");
    13             }
    14             catch (Exception ex)
    15             {
    16 
    17                 throw;
    18             }
    19             return View();
    20         }
    21 
    22 
    23         public async Task<int> Test1()
    24         {
    25             return await Task.Run(() =>
    26             {
    27                 for (int i = 0; i < 10; i++)
    28                 {
    29                     Thread.Sleep(1000);
    30                     System.Diagnostics.Debug.WriteLine($"子①====线程{Thread.CurrentThread.ManagedThreadId}打印{i}");
    31                 }
    32                 return 100;
    33             });
    34         }
    35 
    36         public async Task<int> Test2()
    37         {
    38             return await Task.Run(() =>
    39             {
    40 
    41                 for (int i = 0; i < 10; i++)
    42                 {
    43                     Thread.Sleep(1000);
    44                     System.Diagnostics.Debug.WriteLine($"子②====线程{Thread.CurrentThread.ManagedThreadId}打印{i}");
    45                 }
    46                 return 200;
    47             });
    48         }
    Index执行时大家觉得怎么样 返回300 是吧,我也这样以为了,但是实践是检验结果的唯一方式,程序执行后出错
    “计算函数 result.get 超时 需要以不安全的方式中止”
    呃。。。之前我确实是用例很多 async task 了 上面代码也没错 先并行执行,然后再去结果计算,但这样是不行的 因为
    微软考虑到线程间切换如何保证程序的执行顺序不错乱
    大家可以参考下这篇文章

    https://www.cnblogs.com/OpenCoder/p/4434574.html

    上面内容的大致意思就是说在使用await and async模式时,await关键字这一行后面的代码块会被一个context(也就是上面提到的ASP.NET request contex和UI context)线程继续执行,
    如果我们将本例中调用top-level method的线程称为线程A(即context线程),由于GetJsonAsync方法也是由线程A调用的,所以当GetJsonAsync方法中await的GetStringAsync方法执行完毕后,
    GetJsonAsync需要重新使用线程A执行await代码行之后的代码,而现在由于线程A在top-level method的代码中因为访问了jsonTask.Result被阻塞了
    (因为线程A调用top-level method代码中jsonTask.Result的时候,await的GetStringAsync的Task还没执行完毕,所以被线程A阻塞),
    所以GetJsonAsync无法重新使用线程A执行await代码行之后的代码块,也被阻塞,所以形成了死锁。也就是说top-level method代码中线程A因为
    等待GetJsonAsync中await的GetStringAsync结束被阻塞,而GetStringAsync也等待线程A在top-level method的阻塞结束获得线程A来执行GetJsonAsync中await代码行后面的代码也被阻塞,
    两个阻塞相互等待,相互死锁。

    现在抛出正确写法供各位小伙伴参考
     1  public async Task<ActionResult> Index()
     2         {
     3             System.Diagnostics.Debug.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}begin");
     4            
     5             try
     6             {
     7                 var t1Rlt =  Test1();
     8                 var t2Rlt =  Test2();
     9                 var a = await t1Rlt;
    10                 var b = await t2Rlt;
    11                 System.Diagnostics.Debug.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}结果【{a + b}】");
    12                 System.Diagnostics.Debug.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId}end");
    13             }
    14             catch (Exception ex)
    15             {
    16 
    17                 throw;
    18             }
    19             return View();
    20         }
    21 
    22 
    23         public async Task<int> Test1()
    24         {
    25             return await Task.Run(() =>
    26             {
    27                 for (int i = 0; i < 10; i++)
    28                 {
    29                     Thread.Sleep(1000);
    30                     System.Diagnostics.Debug.WriteLine($"子①====线程{Thread.CurrentThread.ManagedThreadId}打印{i}");
    31                 }
    32                 return 100;
    33             });
    34         }
    35 
    36         public async Task<int> Test2()
    37         {
    38             return await Task.Run(() =>
    39             {
    40 
    41                 for (int i = 0; i < 10; i++)
    42                 {
    43                     Thread.Sleep(1000);
    44                     System.Diagnostics.Debug.WriteLine($"子②====线程{Thread.CurrentThread.ManagedThreadId}打印{i}");
    45                 }
    46                 return 200;
    47             });
    48         }
    执行效果
    截图执行结果是想告诉大家 await 执行结束后 主线程由那个线程接管 是随机的 不知道的小伙伴记住吧




  • 相关阅读:
    客户端及服务端_终端模拟
    安装-homebrew
    客户端及服务端_网页访问
    python server搭建
    ping不通localhost但是可以ping通ip地址
    CentOS7 修改Jenkins以root用户运行
    tomcat配置外部静态资源映射路径(windows和Linux部署)
    Centos 6.10 安装 Jenkins
    axios ios 微信浏览器session问题
    微信公众号 分享接口 签名通过 分享无效果(JSSDK自定义分享接口的策略调整)
  • 原文地址:https://www.cnblogs.com/codedreams/p/10185792.html
Copyright © 2020-2023  润新知