等待(await)会创建另一个线程吗?

3

我阅读了关于async和await的 微软文档。文档中提到:

async 和 await 关键字不会导致创建额外的线程。

但是我运行了下面的代码:

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain
    {
        static void Main()
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            Task task = Test();
            task.Wait();
        }

        public async static Task Test()
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            await Task.Delay(1000);
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        }

    }
}

结果是

1
1
4

很明显,

await Task.Delay(1000);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

这段代码在另一个线程中运行。为什么文档说没有创建线程呢?

我已经阅读了之前的问题“使用async/await会创建新线程吗?”,但它并没有回答我的问题,那个答案只是从Microsoft源代码中复制粘贴过来的。为什么我在测试中看到不同的线程ID?


2
但我认为这是因为您也在使用Tasks,它明确地创建了一个新的后台线程。Async/await可以在没有它们的情况下使用。单独使用它们不会导致创建新线程。就我所理解的而言,它们通过利用多核处理器中的结构来实现效果。 - ADyson
2
@ADyson 不需要使用多个线程来使用任务。任务代表异步操作。使用另一个线程是使某些操作异步的一种可能方式,但不是唯一的方式。您可以(而且许多人都这样做)在完全单线程的应用程序中使用任务。 - Servy
3
await 不会产生工作线程,这并不意味着没有工作线程被创建!在异步工作流中有很多东西可以创建工作线程,但 await 不是其中之一。你应该问的问题是:在控制台应用程序中,等待任务的继续是否可以安排到工作线程上? 答案是“可以”。 - Eric Lippert
3
如果你为了学习而尝试使用async不要在控制台应用程序中这样做。通常,控制台应用程序是更容易学习的环境,但对于async来说并非如此,因为在控制台应用程序中没有消息循环来管理异步操作。请使用具有顶级消息循环以处理异步方法的项目类型,例如winforms或wpf应用程序。 - Eric Lippert
1
你说延迟在另一个线程上运行是“显而易见”的,但既然这是错误的,那么你相信某些错误的东西是显然正确的。你对世界的感知有问题。这里真正需要注意的是:当假的事情既不明显也不正确时,你停止相信假的事情是“显然正确”的话,你会更成功。 - Eric Lippert
显示剩余5条评论
2个回答

9

我总是鼓励人们阅读我的async入门介绍,然后再查看async最佳实践。简而言之:

await默认捕获“上下文”,并在该上下文中恢复执行async方法。在这种情况下,上下文是线程池上下文。所以,这就是为什么你看到Test会在线程池线程上继续执行。

asyncawait本身不会创建任何额外的线程;例如,在UI应用程序中做同样的操作,“上下文”将是UI线程,Test将在该UI线程上继续执行。但是,await隐式捕获的上下文负责调度asnc继续,而此示例中的上下文只是将其安排到线程池中。


1
你在测试中看到不同的线程ID是因为使用了不同的线程来执行代码。这并不意味着异步/等待导致了额外线程的创建。在这种情况下,已经存在于一个称为线程池的线程池中的线程被使用。
有两个不同的概念:
1. 表示要执行的工作的任务,和 2. 任务如何实际处理(例如,调度和执行)
Async/await与前者相关,但你的测试代码告诉你有关后者的内容:处理这些任务的具体机制。
该机制由上下文环境决定。一些上下文环境使用多个线程(但请注意,它取决于上下文环境决定如何管理这些线程,包括是否以及何时创建新线程),但有些只使用1个线程。
使用多个线程与异步/等待无关,而与上下文环境有关。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接