我正在调查异步,我遇到了以下无法解释的结果。
以下代码(可以复制/粘贴到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}},结果现在只有两个线程正在运行!也许这可能是因为线程池重新使用线程?
为什么这里有三个不同的线程在运作?
答案 0 :(得分:3)
async
方法分为几部分工作,在这种情况下,所有工作都安排在线程池中。 await
指定async
方法可能被“破坏”的地方。
第一个线程是主线程,它不是线程池的一部分。
第二个线程是用于执行传递给Task.Run
的委托的线程。这可以是线程池中的任何线程。
第三个线程是用于在Wait
之后获取await
执行的线程。这也可以是线程池中的任何线程。
从概念上讲,这就是发生的事情。实际上,它有点复杂:
我认为首先用Thread.Sleep
解释会更容易。在这种情况下,Wait
在await
完成之前已被Task.Run
完全暂停。当Task.Run
完成时,它会执行其延续,并且async
方法的延续设置为ExecuteSynchronously
(described on my blog)。因此,运行Thread.Sleep
的同一个线程实际上继续执行Wait
而不会屈服。
您的原始代码存在竞争条件:Task.Run
非常短,因此可以快速完成。您看到的是Wait
检查Task
并查看它尚未完成并暂停Wait
方法。在此期间,Task.Run
完成并将Wait
的剩余部分安排到线程池。
如果您想详细了解await
如何使用上下文,我建议使用async
intro。你通常不必担心这样的细节。