11

让我发布一个简单的例子:

    private void MyMethod()
    {
        Task task = MyAsyncMethod();
        task.Wait();
    }

    private async Task MyAsyncMethod()
    {
        //Code before await
        await MyOtherAsyncMethod();
        //Code after await
    }

假设我在单线程应用程序(如控制台应用程序)中运行上述代码。我很难理解代码如何//Code after await运行。

我知道当我点击控制中的await关键字时MyAsyncMethod()会回到MyMethod(),但随后我将使用 . 锁定线程task.Wait()//Code after await如果线程被锁定,如果应该接受它 的线程被锁定,怎么能运行?

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

我不确定这应该如何工作?

4

2 回答 2

15

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

但在控制台应用程序中,这是可行的。但如何?

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

在 winform 应用程序SynchronizationContext.Current将设置为WindowsFormsSynchronizationContext将发布到“消息循环”的调用,但谁来处理呢?out 主线程正在等待Wait()

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

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

于 2013-10-23T13:56:57.220 回答
14

是否创建了一个新线程来运行//等待后的代码?

也许。也许不吧。可等待模式实现使用在等待表达式开始时为“当前”的同步上下文Task运行延续(表达式后面的位)。await例如,如果你在一个 UI 线程的上下文中,这意味着你最终会回到同一个 UI 线程。如果你在一个线程池线程中,你最终会回到某个线程池线程,但它可能是一个不同的线程。

当然,对于您的代码示例,如果您在 UI 线程中,您的调用Wait()将阻塞 UI 线程,因此无法继续运行 - 您需要注意这一点。(调用Wait()Result处理您不知道要完成的任务,并且可能需要在当前线程上工作,这是一个坏主意。)

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

await task.ConfigureAwait(false);

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

我认为通过 await 熟悉引擎盖下发生的事情是个好主意。网上有很多文档,如果您允许我做一个简短的介绍,还有第3 版 C# in Depth和我关于该主题的 Tekpub 截屏视频系列。或者从MSDN开始,然后从那里开始。

于 2013-10-23T13:47:06.907 回答