什么线程在`await`关键字后运行代码?

我先发一个简单的例子:

private void MyMethod() { Task task = MyAsyncMethod(); task.Wait(); } private async Task MyAsyncMethod() { //Code before await await MyOtherAsyncMethod(); //Code after await } 

假设我在单线程应用程序中运行上述代码,就像控制台应用程序一样。 我很难理解代码//Code after await的代码是如何运行的。

据我所知,当我在MyAsyncMethod()控件中点击await关键字时,会返回到MyMethod() ,但后来我用task.Wait()锁定了该线程。 如果线程被锁定,那么如果应该接受它的线程被锁定,那么//Code after await运行//Code after await怎么办?

是否创建了一个新线程来//Code after await ? 或者主线程是否神奇地走出task.Wait()运行//Code after await

我不确定这是怎么回事?

如果从主线程调用,则在Winform App中发布的代码将为“死锁”,因为您使用Wait()阻止主线程。

但在控制台应用程序中这是有效 但是怎么样?

答案隐藏在SynchronizationContext.Currentawait捕获“SynchronizationContext”,当任务完成时,它将继续在相同的“SynchronizationContext”中。

在winform应用程序中, SynchronizationContext.Current将被设置为WindowsFormsSynchronizationContext ,它将发布到“消息循环”的调用,但是谁将要处理它? out主线程在Wait()Wait()

在控制台应用程序中,默认情况下不会设置SynchronizationContext.Current ,因此当没有“SynchronizationContext”可用于等待捕获时它将为null ,因此它将调度继续到ThreadPool (TaskScheduler.Default,这是ThreadpoolTask​​Scheduler),因此等待后的代码工作(通过线程池线程)。

可以使用Task.ConfigureAwait(false);来控制上述捕获行为Task.ConfigureAwait(false); 这将阻止winform应用程序死锁,但await后的代码不再在UI线程中运行。

是否创建了一个新线程来等待?

也许。 也许不吧。 Task的等待模式实现使用在await表达式开始时为“current”的同步上下文运行continuation( await表达式之后的位)。 例如,如果您处于UI线程的上下文中,则意味着您将最终返回到相同的UI线程。 如果你是一个线程池线程,你最终会回到一些线程池线程,但它可能是一个不同的线程。

当然,使用您的代码示例,如果您在UI线程中,您对Wait()调用将阻止UI线程,以便继续无法运行 – 您需要注意这一点。 (对您不知道要完成的任务调用Wait()Result ,以及哪些可能需要在当前线程上工作,这是一个坏主意。)

请注意,您可以调用Task.ConfigureAwait以便表达不需要继续在相同上下文中的意图。 这通常适用于不关心它们运行在哪个线程上的库方法:

 await task.ConfigureAwait(false); 

(它影响的不仅仅是线程 – 它是捕获与否的整个上下文。)

我认为通过等待来熟悉引擎盖下的内容是个好主意。 网上有很多文档,如果你能给我一个简短的插件,那么还有第三版的C#深度 ,以及关于这个主题的Tekpub截屏系列 。 或者从MSDN开始,然后从那里开始。