最近項目數據量較大,使用 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 執行結束后 主線程由那個線程接管 是隨機的 不知道的小伙伴記住吧