範例程式码:
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 程式锁死问题解析