调用异步方法涉及三个线程

时间:2013-07-01 17:27:57

标签: c# .net async-await

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

以下代码(可以复制/粘贴到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

显示涉及三个主题。

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

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

1 个答案:

答案 0 :(得分:3)

async方法分为几部分工作,在这种情况下,所有工作都安排在线程池中。 await指定async方法可能被“破坏”的地方。

第一个线程是主线程,它不是线程池的一部分。

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

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

从概念上讲,这就是发生的事情。实际上,它有点复杂:

我认为首先用Thread.Sleep解释会更容易。在这种情况下,Waitawait完成之前已被Task.Run完全暂停。当Task.Run完成时,它会执行其延续,并且async方法的延续设置为ExecuteSynchronouslydescribed on my blog)。因此,运行Thread.Sleep的同一个线程实际上继续执行Wait而不会屈服。

您的原始代码存在竞争条件:Task.Run非常短,因此可以快速完成。您看到的是Wait检查Task并查看它尚未完成并暂停Wait方法。在此期间,Task.Run完成并将Wait的剩余部分安排到线程池。

如果您想详细了解await如何使用上下文,我建议使用async intro。你通常不必担心这样的细节。