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