为什么 C++ 的 async/await 不需要事件循环?

dor*_*mon 17 c++ asynchronous coroutine c++20

对于有一些其他语言(Python/JavaScript)异步经验的人来说,在谈论 async/await 时,总是假设某处存在事件循环。但是对于 C++,我已经查看了文档并没有找到任何谈论事件循环的地方。为什么会这样?

对于 Node,它只有一个默认的事件循环。对于 Python,您可以根据需要创建多个。但是对于 C++ 来说,这个事件循环是否像 Node 一样?或者出于某种原因,我们根本不需要它吗?

Nic*_*las 11

Python 和 JavaScript 不执行 CPU 线程(好吧,Python 可以执行线程,但这与此处无关,因为 Python 的线程内容本身并不是可等待的)。所以他们用“事件循环”伪造线程。

C++ 是一种低级语言,它知道什么是 CPU 线程并期望使用它们。因此,当你co_await使用某个表达式时,您通常正在等待一个(可能)发生在另一个线程中的进程。

您当然可以使用事件循环来伪造线程,并创建一些使用事件循环处理的可等待类型。但这通常不是 C++ 程序员对异步处理所做的。

在 C++ 协程中,恢复执行协程的工作方式完全取决于您使用的可等待类型。它管理函数恢复的调度。C++ 中的大多数 awaitable 将使用某种形式的 CPU 线程(最常见的方法是在执行它正在等待的异步进程的线程上调用协程)。其他人可能有某种事件循环或其他什么。但关键是,这是产生您正在等待的值的事物的函数,而不是协程本身。


Gui*_*cot 10

C++ 中没有事件循环,线程与协程也没有什么关系。

当您co_await使用 C++ 时,函数的执行被挂起,代码继续执行调用者,就像函数已返回一样。其实就是这样实现的。co_await将更改协程的内部状态机并返回。

The execution is resumed when the code explicitly resumes the function.

This is cooperative multitasking. Control of the execution is explicit and predictable.

Now, using most libraries you won't necessarily have to call back the coroutine to be resumed. This is where executors comes in. They are a bit like event loops but inside a library instead of baked in the language. User code can implement them as well, and you can have different one for different use cases. They will usually schedule the execution of coroutines and can also manage multiple threads to execute many of them at once.

For example, you could totally implement an executor on top of a thread pool. Large operation that wait on io won't need to block the thread for themselves, it will start the io operation and give back the thread to other tasks. Internally, the io operation will schedule the coroutine back into the thread pool to be resumed.

Another example would be io_uring on linux, which is the new async io api. One could wrap the facility with an executor and run io operation as coroutines. Technically, you don't need threads to do this one. Calls to co_await will simply schedule the io operation and the coroutine will resume once the kernel has enqueued a result.


Vic*_*lea 7

协程不需要事件循环。

当电脑读取 co_await时会发生什么,它将跳转到调用协程的函数并保存其所有帧(局部变量值等)。

这里的神奇之处在于,下次你调用协程时,你会回到这个状态。正如你所看到的,不需要事件循环,只需要一个地方来存储这个frame