1、await和.result/ .getwaiter() .getresult()的區別
await:Task.Run里面的邏輯是新開的執行緒去執行的,await Task.Run后面邏輯都在新開的執行緒去執行,
private async void MainWindow_Loaded(object sender, RoutedEventArgs e) { Task.Run(async () => { // 執行順序:1,執行緒:3 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); await TaskWait(); // 執行順序:5,執行緒:4 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }); } private async Task TaskWait() { Thread.Sleep(1000); // 執行順序:2,執行緒:3 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); await Task.Run(() => { Thread.Sleep(1000); // 執行順序:3,執行緒:4 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }); // 執行順序:4,執行緒:4 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }
但是呼叫執行緒是UI執行緒時,情況不一樣;除了Task.Run里面的邏輯是新開的執行緒,后面的邏輯仍然使用UI執行緒去執行的,并且呼叫后面的邏輯需要等await里面的邏輯執行完畢才繼續執行,(執行順序與執行緒類似于.result)
private async void MainWindow_Loaded(object sender, RoutedEventArgs e) { // 執行順序:1,執行緒:1 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); await TaskWait(); // 執行順序:5,執行緒:1 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); } private async Task TaskWait() { Thread.Sleep(1000); // 執行順序:2,執行緒:1 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); await Task.Run(() => { Thread.Sleep(1000); // 執行順序:3,執行緒:3 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }); // 執行順序:4,執行緒:1 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }
.result/ .getwaiter() .getresult():除了Task.Run里面的邏輯是新開的執行緒,后面的邏輯仍然使用呼叫執行緒去執行的,并且呼叫后面的邏輯需要等await里面的邏輯執行完畢才繼續執行,
private async void MainWindow_Loaded(object sender, RoutedEventArgs e) { Task.Run(async () => { // 執行順序:1,執行緒:3 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); await TaskWait(); // 執行順序:5,執行緒:3 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }); } private async Task TaskWait() { Thread.Sleep(1000); // 執行順序:2,執行緒:3 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); Task.Run(() => { Thread.Sleep(1000); // 執行順序:3,執行緒:4 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }).GetAwaiter().GetResult(); // 執行順序:4,執行緒:3 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }
2、在UI執行緒使用Wait等待await Task.Run造成死鎖問題,呼叫部分阻塞自己所在的執行緒,即主執行緒,直到Task物件“完成”,而回傳的Task物件要想“完成”,必須在主執行緒上執行await之后的代碼,而主執行緒早就處于阻塞狀態,它在等待Task物件完成!于是死鎖就產生了,如果不是UI執行緒則不會阻塞,就不會出現死鎖,所以使用Task.Run的時候最好await到底,中間不要切換成Wait()、.Result,
private async void MainWindow_Loaded(object sender, RoutedEventArgs e) { TaskWait().Wait(); } private async Task TaskWait() { await Task.Run(() => { }); }
如果實在要用的話,需要await Task.Run(() =>{}).ConfigureAwait(false),使得await Task.Run之后的代碼不回到UI執行緒執行,直接使用Task.Run新創建的執行緒執行,由于不在UI執行緒了,必須保證執行緒安全,
private async void MainWindow_Loaded(object sender, RoutedEventArgs e) { TaskWait().Wait(); } private async Task TaskWait() { await Task.Run(() => { // 執行緒:3 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }).ConfigureAwait(false); // 執行緒:3 Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }
3、Task.Run如果不使用await或者wait()等等待操作,外層的trycatch是沒辦法捕獲Task.Run里面的例外的,
例如下面的例子就沒辦法捕獲到例外
try { Task.Run(() => { var j = 0; var i = 1 / j; }); } catch (Exception ex) { Console.WriteLine(ex.Message); }
下面的例子就可以捕獲到例外
try { await Task.Run(() => { var j = 0; var i = 1 / j; }); } catch (Exception ex) { Console.WriteLine(ex.Message); }
4、MVVM中除了ObservableCollection依賴屬性,其它依賴屬性不需要在UI執行緒賦值查詢操作,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/501022.html
標籤:C#
