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 执行结束后 主线程由那个线程接管 是随机的 不知道的小伙伴记住吧




posted @ 2018-12-27 16:36  @_追寻_@  阅读(3711)  评论(2编辑  收藏  举报