iT邦幫忙

1

C# await/async 程式鎖死問題解析

範例程式碼:

         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 程式鎖死問題解析


尚未有邦友留言

立即登入留言