範例程式碼:
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine($"SynchronizationContext.Current = {SynchronizationContext.Current}");
Console.WriteLine($"程式開始 執行緒ID:{Thread.CurrentThread.ManagedThreadId}");
var imng = ImageService();
Console.WriteLine($"等待ImageService結果 執行緒ID: {Thread.CurrentThread.ManagedThreadId}");
var result = imng.Result;
Console.WriteLine($"讀取到的結果 : {result}");
}
private async Task<string> ImageService()
{
Console.WriteLine($"ImageService 準備開始 執行緒ID: {Thread.CurrentThread.ManagedThreadId}");
await Task.Run(() =>
{
Console.WriteLine($"Task.Run 執行緒 ID: {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(100);
Console.WriteLine("程式已跑完");
});
Console.WriteLine($"ImageService 準備開始 執行緒ID: { Thread.CurrentThread.ManagedThreadId}");
return "程式結束";
}
在 Windows Forms、WPF、和 ASP.NET 這類有 UI 的程式中,會使用SynchronizationContext
來存放UI執行緒的相關訊息,方便呼叫 await 敘述做完要往下繼續做的時候能夠切換為主執行緒繼續完成任務。
如上圖主執行緒 會依照程式順序執行(a) (b)到await的時候會開一個執行續讓他執行裡面要等待的運算,然後主執行緒就會往下繼續做,當主執行緒執行到(e)的時候他就會,等待ImageService回傳的結果,所以這時候1號執行緒是忙碌中的,這時候(c)裡面的程式做完了要,回來做(d)的時候,如果SynchronizationContext不是null就會由SynchronizationContext裡面紀錄的執行緒來完成(d)的任務,但這時 1號執行緒是忙碌的狀態,要等(e)執行完才能開始做(d)的工作
因此變成(e)在等ImageService的結果,(d)在等(e)做完工作,形成了互相等的狀態。
在Console不會有這情況的原因是,在Console程式中SynchronizationContext.Current=null,
所以在await完成後要往下個任務繼續做的時候,會是由TaskScheduler(工作排程器)物件,來決定哪個執行緒去完成這項任務
同步發佈至個人部落格:
await/async 程式鎖死問題解析