await Task与Task.GetAwaiter差异及Dispatcher.RunAsync执行问题解析
为什么两段Dispatcher代码执行行为不同?以及await Task.Delay与同步等待的区别
咱们来一步步拆解你的问题,先分析两段代码的执行逻辑差异,再讲清楚两种等待方式的核心区别。
一、第一段代码为何两个文本同时变更?
先看第一段代码的核心逻辑:
Task.Run(async () => { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => { await Task.Delay(2000); textnew1.Text = "Dispatched"; }); await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => { await Task.Delay(2000); textnew2.Text = "BackgroundTask"; }); });
这里的关键问题出在**Dispatcher.RunAsync对async void委托的处理**:
- 你传给
Dispatcher.RunAsync的是一个async void的lambda(因为它是async且没有返回值)。 Dispatcher.RunAsync的设计是:它会把这个委托放到UI线程的队列中执行,但当委托内部遇到await Task.Delay(2000)时,这个async void委托会立即返回,此时await Dispatcher.RunAsync(...)就认为已经完成了,会马上执行第二个Dispatcher.RunAsync调用。- 结果就是两个UI线程的委托几乎同时启动,各自内部异步等待2秒后修改文本,最终两个文本会同时变更。
二、第二段代码为何能依次执行?
再看第二段代码:
Task.Run(async () => { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { Task.Delay(2000).GetAwaiter().GetResult(); textnew1.Text = "Dispatched"; }); await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { Task.Delay(2000).GetAwaiter().GetResult(); textnew2.Text = "BackgroundTask"; }); });
这段代码的行为符合预期,原因是:
- 你传给
Dispatcher.RunAsync的是一个同步委托(没有async修饰)。 Dispatcher.RunAsync会等待这个同步委托完全执行完毕后,才会让外部的await继续。- 而
Task.Delay(2000).GetAwaiter().GetResult()是同步阻塞当前线程(这里是UI线程),它会卡住UI线程2秒,直到延迟完成才修改textnew1.Text,然后第一个Dispatcher.RunAsync完成,外部的await结束,再执行第二个Dispatcher.RunAsync,同样阻塞UI线程2秒后修改textnew2.Text,所以是依次变更。
⚠️ 注意:这种在UI线程同步阻塞的方式非常不推荐,会导致界面失去响应,用户体验极差!
三、await Task.Delay与Task.Delay().GetAwaiter().GetResult()的核心区别
这两种方式的本质是异步等待 vs 同步阻塞:
await Task.Delay(2000):- 这是异步等待的正确方式,它会释放当前线程(让线程去处理其他任务,比如UI线程可以继续响应用户操作)。
- 2秒延迟结束后,会回到当前上下文(比如UI线程)继续执行后续代码。
- 要求所在方法是
async修饰,并且返回Task/Task<T>(如果是async void,调用者无法等待它完成,这也是第一段代码出问题的核心)。
Task.Delay(2000).GetAwaiter().GetResult():- 这是同步阻塞的方式,它会卡死当前线程,直到延迟任务完成。
- 它和
Task.Wait()类似,但能直接抛出原始异常(而不是包装成AggregateException)。 - 在UI线程中使用会导致界面冻结,完全违背异步编程的初衷,除非万不得已绝对不要这么做。
正确的写法(既异步又依次执行)
如果想实现“第一个文本先变更,两秒后第二个文本变更”的效果,同时不阻塞UI线程,正确的写法应该是在同一个Dispatcher委托中异步等待:
Task.Run(async () => { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => { textnew1.Text = "Dispatched"; await Task.Delay(2000); textnew2.Text = "BackgroundTask"; }); });
或者如果需要分开Dispatcher调用,应该让内部委托返回Task,使用AsTask()把IAsyncAction转为可等待的Task:
Task.Run(async () => { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => { await Task.Delay(2000); textnew1.Text = "Dispatched"; }).AsTask(); // 等待整个async委托执行完毕 await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => { await Task.Delay(2000); textnew2.Text = "BackgroundTask"; }).AsTask(); });
内容的提问来源于stack exchange,提问作者A6EE
相关产品推荐
相关产品推荐

