await Task.Yield()和await Task.CompletedTask有什麼不同
阿新 • • 發佈:2020-01-16
有時候我們在程式碼中要執行一些非常耗時的操作,我們不希望這些操作阻塞呼叫執行緒(主執行緒)的執行,因為呼叫執行緒(主執行緒)可能還有更重要的工作要做,我們希望將這些非常耗時的操作由另外一個執行緒去執行,這個時候就可以用到await Task.Yield(),它藉助了C# 5.0中的非同步函式關鍵字await async,將await關鍵字之後的程式碼交由執行緒池中的另一個執行緒執行(前提是專案的SynchronizationContext.Current為null)。
那麼有同學肯能會納悶,await Task.Yield()和await Task.CompletedTask有什麼不同嗎?
它倆可大不一樣
- Task.CompletedTask本質上來說是返回一個已經完成的Task物件,所以這時如果我們用await關鍵字去等待Task.CompletedTask,.NET Core認為沒有必要再去執行緒池啟動一個新的執行緒來執行await關鍵字之後的程式碼,所以實際上await Task.CompletedTask之前和之後的程式碼是在同一個執行緒上同步執行的,通俗易懂的說就是單執行緒的。這也是為什麼很多文章說,使用了await async關鍵字並不代表程式就變成非同步多執行緒的了。
- 而Task.Yield()就不一樣了,我們可以理解Task.Yield()是真正使用Task來啟動了一個執行緒,只不過這個執行緒什麼都沒有幹,相當於在使用await Task.Yield()的時候,確實是在用await等待一個還沒有完成的Task物件,所以這時呼叫執行緒(主執行緒)就會立即返回去做其它事情了,當呼叫執行緒(主執行緒)返回後,await等待的Task物件就立即變為完成了,這時await關鍵字之後的程式碼由另外一個執行緒池執行緒來執行。
下面我用.NET Core控制檯專案,寫一個示例程式碼來演示await Task.Yield()和await Task.CompletedTask的不同:
using System; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; namespace NetCoreTaskYield { class Program { /// <summary> /// TaskYield使用await Task.Yield(),是真正的非同步執行,await關鍵字之前和之後的程式碼使用不同的執行緒執行 /// </summary> static async Task TaskYield() { Console.WriteLine("TaskYield before await, current thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString()); await Task.Yield();//執行到await Task.Yield()時,呼叫TaskYield()方法的執行緒(主執行緒)立即就返回了,await關鍵字後面的程式碼實際上是由另一個執行緒池執行緒執行的 //注意Task.Yield()方法返回的不是Task類的物件,而是System.Runtime.CompilerServices.YieldAwaitable類的物件 Console.WriteLine("TaskYield after await, current thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString()); Thread.Sleep(3000);//阻塞執行緒3秒鐘,模擬耗時的操作 Console.WriteLine("TaskYield finished!"); } /// <summary> /// 模擬TaskYield的非同步執行 /// </summary> static Task TaskYieldSimulation() { //模擬TaskYield()方法中,await關鍵字之前的程式碼,由呼叫TaskYieldSimulation()方法的執行緒(主執行緒)執行 Console.WriteLine("TaskYieldSimulation before await, current thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString()); return Task.Run(() => { //使用Task.Run啟動一個新的執行緒什麼都不做,立即完成,相當於就是Task.Yield() }).ContinueWith(t => { //下面模擬的是TaskYield()方法中,await關鍵字之後的程式碼,由另一個執行緒池執行緒執行 Console.WriteLine("TaskYieldSimulation after await, current thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString()); Thread.Sleep(3000);//阻塞執行緒3秒鐘,模擬耗時的操作 Console.WriteLine("TaskYieldSimulation finished!"); }); } /// <summary> /// TaskCompleted使用await Task.CompletedTask,是假的非同步執行,實際上是同步執行,await關鍵字之前和之後的程式碼使用相同的執行緒執行 /// </summary> static async Task TaskCompleted() { Console.WriteLine("TaskCompleted before await, current thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString()); await Task.CompletedTask;//執行到await Task.CompletedTask時,由於await的Task.CompletedTask已經處於完成狀態,所以.NET Core判定await關鍵字後面的程式碼還是由呼叫TaskCompleted()方法的執行緒(主執行緒)來執行,所以實際上整個TaskCompleted()方法是單執行緒同步執行的 Console.WriteLine("TaskCompleted after await, current thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString()); Thread.Sleep(3000);//阻塞執行緒3秒鐘,模擬耗時的操作 Console.WriteLine("TaskCompleted finished!"); } static void Main(string[] args) { Console.WriteLine("Main thread id: {0}", Thread.CurrentThread.ManagedThreadId.ToString()); Console.WriteLine("=============================================="); TaskYield().Wait(); Console.WriteLine("=============================================="); TaskCompleted().Wait(); Console.WriteLine("=============================================="); TaskYieldSimulation().Wait(); Console.WriteLine("Press any key to end..."); Console.ReadKey(); } } }
執行結果如下所示:
注意TaskYield()方法是真正的非同步執行,TaskYieldSimulation()方法模擬演示了await Task.Yield()非同步執行的原理,而TaskCompleted()方法是假的非同步執行,實則為同步單執行緒執行。
參考文獻:
終於明白了 C# 中 Task.Yield 的用途
await Task.Yield(); 超簡單理解!
&n