【问题标题】:Three threads involved in calling an async method调用异步方法涉及的三个线程
【发布时间】:2013-07-01 17:27:56
【问题描述】:

我正在调查异步,我遇到了以下我无法解释的结果。

以下代码(可以复制/粘贴到 Linqpad 或类似代码中)给我带来了使用线程池中的三个线程的令人惊讶的结果。

void Main()
{
    Wait();
}

public async void Wait()
{
    Print ("Wait() called. Calling GetAnswer()");
    var t = await GetAnswerAsync3();
    Print("Result of Wait(): " + t);
}

public Task<bool> GetAnswerAsync3()
{
    return Task.Run(() => {
            // Thread.Sleep(1000);
        Print("GetAnswerAsync3() called");
        return true;
    });
}

public void Print(string message)
{
    Console.WriteLine ("Thread: " + Thread.CurrentThread.ManagedThreadId + " - " + message);
}

结果是:

Thread: 35 - Wait() called. Calling GetAnswer()
Thread: 51 - GetAnswerAsync3() called
Thread: 43 - Result of Wait(): True

显示涉及三个线程。

现在。如果我在GetAnswerAsync3 返回的任务返回之前添加Thread.Sleep(1000),结果现在只有两个线程在运行!也许这可能是因为线程池重用了一个线程?

为什么这里有三个不同的线程在起作用?

【问题讨论】:

    标签: c# .net async-await


    【解决方案1】:

    async 方法被分解为多个工作,在这种情况下,这些工作都被调度到线程池中。 await 指定async 方法可能被“破坏”的地方。

    第一个线程是主线程,不属于线程池。

    第二个线程用于执行传递给Task.Run 的委托。这可以是线程池中的任何线程。

    第三个线程是用于在Wait 之后执行Wait 的线程。这也可以是线程池中的任何线程。

    从概念上讲,就是这样。实际上,它有点复杂:

    我认为首先使用Thread.Sleep 更容易解释。在这种情况下,WaitTask.Run 完成之前已被其await 完全挂起。当Task.Run 完成时,它会执行其延续,并且async 方法的延续具有ExecuteSynchronously 集(described on my blog)。所以运行Thread.Sleep 的同一个线程实际上继续执行Wait 而没有产生。

    您的原始代码有一个竞争条件:Task.Run 非常短,所以它会很快完成。您看到的是Wait 检查Task 并看到它尚未完成并暂停Wait 方法。与此同时,Task.Run 完成并将Wait 的剩余部分调度到线程池中。

    如果您想了解更多关于 await 如何与上下文配合使用的信息,我推荐我的 async intro。你通常不必担心这样的细节。

    【讨论】:

    • 这是有道理的。为什么添加Thread.Sleep()后会发生变化?
    • 如果没有Sleep,则会出现竞争条件,即当将继续安排到第三个线程时,第二个线程仍被视为忙于工作。对于Sleep,该方法在Task 完成时已经挂起,因此(作为优化)第二个线程将直接执行延续。
    • 如果我在Sleep 就位的情况下,将ConfigureAwait(false) 添加到GetAnswerAsync(),为什么不会再出现三个线程?
    • ConfigureAwait(false) 表示你不关心当前上下文是否被await 捕获并用于继续该方法。它确实不是的意思是“强制将其安排在线程池上而不是直接执行”。
    • 我在我的async intro 中介绍了这一点 - 请再次阅读。默认情况下,await 将捕获当前的SynchronizationContext(除非它是null,在这种情况下它使用当前的TaskScheduler),并且它将使用该上下文来恢复async 方法。因此,如果这是一个 UI 应用程序,它将返回到 UI 线程。控制台应用没有 SyncCtx,但有 you can provide one of your own
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-29
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多