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 執行結束后 主線程由那個線程接管 是隨機的 不知道的小伙伴記住吧





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM