C++20 中的协程是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/43503656/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What are coroutines in C++20?
提问by Pavan Chandaka
What are coroutines in c++20?
c++20中的协程是什么?
In what ways it is different from "Parallelism2" or/and "Concurrency2" (look into below image)?
它在哪些方面与“Parallelism2”或/和“Concurrency2”(查看下图)不同?
The below image is from ISOCPP.
下图来自 ISOCPP。
https://isocpp.org/files/img/wg21-timeline-2017-03.png
回答by Yakk - Adam Nevraumont
At an abstract level, Coroutines split the idea of having an execution state off of the idea of having a thread of execution.
在抽象层面上,协程将拥有执行状态的想法与拥有执行线程的想法分开。
SIMD (single instruction multiple data) has multiple "threads of execution" but only one execution state (it just works on multiple data). Arguably parallel algorithms are a bit like this, in that you have one "program" run on different data.
SIMD(单指令多数据)有多个“执行线程”但只有一个执行状态(它只适用于多个数据)。可以说并行算法有点像这样,因为你有一个“程序”在不同的数据上运行。
Threading has multiple "threads of execution" and multiple execution states. You have more than one program, and more than one thread of execution.
线程有多个“执行线程”和多个执行状态。你有不止一个程序,不止一个执行线程。
Coroutines has multiple execution states, but does not own a thread of execution. You have a program, and the program has state, but it has no thread of execution.
协程有多个执行状态,但不拥有一个执行线程。你有一个程序,程序有状态,但它没有执行线程。
The easiest example of coroutines are generators or enumerables from other languages.
协程最简单的例子是来自其他语言的生成器或枚举。
In pseudo code:
在伪代码中:
function Generator() {
for (i = 0 to 100)
produce i
}
The Generator
is called, and the first time it is called it returns 0
. Its state is remembered (how much state varies with implementation of coroutines), and the next time you call it it continues where it left off. So it returns 1 the next time. Then 2.
将Generator
被调用,并在第一时间调用时返回0
。它的状态被记住(多少状态随着协程的实现而变化),下次你调用它时它会从上次停止的地方继续。所以下次它返回1。然后 2。
Finally it reaches the end of the loop and falls off the end of the function; the coroutine is finished. (What happens here varies based on language we are talking about; in python, it throws an exception).
最后到达循环的末尾,从函数的末尾掉下来;协程完成。(这里发生的事情因我们所谈论的语言而异;在 python 中,它会引发异常)。
Coroutines bring this capability to C++.
协程为 C++ 带来了这种能力。
There are two kinds of coroutines; stackful and stackless.
有两种协程;堆叠和无堆叠。
A stackless coroutine only stores local variables in its state and its location of execution.
无堆栈协程仅在其状态和执行位置中存储局部变量。
A stackful coroutine stores an entire stack (like a thread).
堆栈式协程存储整个堆栈(如线程)。
Stackless coroutines can be extremely light weight. The last proposal I read involved basically rewriting your function into something a bit like a lambda; all local variables go into the state of an object, and labels are used to jump to/from the location where the coroutine "produces" intermediate results.
无堆栈协程可以非常轻量级。我读到的最后一个提案基本上涉及将您的函数重写为有点像 lambda 的东西;所有局部变量都进入对象的状态,标签用于跳转到/从协程“产生”中间结果的位置。
The process of producing a value is called "yield", as coroutines are bit like cooperative multithreading; you are yielding the point of execution back to the caller.
产生值的过程称为“产量”,因为协程有点像协作多线程;您将执行点交还给调用者。
Boost has an implementation of stackful coroutines; it lets you call a function to yield for you. Stackful coroutines are more powerful, but also more expensive.
Boost 有一个堆栈协程的实现;它可以让你调用一个函数来为你让步。Stackful 协程更强大,但也更昂贵。
There is more to coroutines than a simple generator. You can await a coroutine in a coroutine, which lets you compose coroutines in a useful manner.
协程不仅仅是一个简单的生成器。您可以在协程中等待协程,这使您可以以有用的方式组合协程。
Coroutines, like if, loops and function calls, are another kind of "structured goto" that lets you express certain useful patterns (like state machines) in a more natural way.
协程,如 if、循环和函数调用,是另一种“结构化转到”,它可以让您以更自然的方式表达某些有用的模式(如状态机)。
The specific implementation of Coroutines in C++ is a bit interesting.
Coroutines在C++中的具体实现有点意思。
At its most basic level, it adds a few keywords to C++: co_return
co_await
co_yield
, together with some library types that work with them.
在最基本的层面上,它向 C++: 添加了一些关键字co_return
co_await
co_yield
,以及一些与它们一起使用的库类型。
A function becomes a coroutine by having one of those in its body. So from their declaration they are indistinguishable from functions.
一个函数通过在其主体中包含一个协程而成为协程。因此,从它们的声明来看,它们与函数没有区别。
When one of those three keywords are used in a function body, some standard mandated examining of the return type and arguments occurs and the function is transformed into a coroutine. This examining tells the compiler where to store the function state when the function is suspended.
当在函数体中使用这三个关键字之一时,会发生一些标准的强制检查返回类型和参数,并将函数转换为协程。这种检查告诉编译器在函数挂起时将函数状态存储在哪里。
The simplest coroutine is a generator:
最简单的协程是一个生成器:
generator<int> get_integers( int start=0, int step=1 ) {
for (int current=start; true; current+= step)
co_yield current;
}
co_yield
suspends the functions execution, stores that state in the generator<int>
, then returns the value of current
through the generator<int>
.
co_yield
挂起函数执行,将该状态存储在 中generator<int>
,然后current
通过返回 的值generator<int>
。
You can loop over the integers returned.
您可以遍历返回的整数。
co_await
meanwhile lets you splice one coroutine onto another. If you are in one coroutine and you need the results of an awaitable thing (often a coroutine) before progressing, you co_await
on it. If they are ready, you proceed immediately; if not, you suspend until the awaitable you are waiting on is ready.
co_await
同时让您将一个协程拼接到另一个协程上。如果您在一个协程中并且在继续之前需要等待事物(通常是协程)的结果,那么您co_await
就可以了。如果他们准备好了,你就立即进行;如果没有,您将暂停,直到您正在等待的等待对象准备就绪。
std::future<std::expected<std::string>> load_data( std::string resource )
{
auto handle = co_await open_resouce(resource);
while( auto line = co_await read_line(handle)) {
if (std::optional<std::string> r = parse_data_from_line( line ))
co_return *r;
}
co_return std::unexpected( resource_lacks_data(resource) );
}
load_data
is a coroutine that generates a std::future
when the named resource is opened and we manage to parse to the point where we found the data requested.
load_data
是一个协程,它std::future
在命名资源打开时生成一个,我们设法解析到我们找到所请求数据的点。
open_resource
and read_line
s are probably async coroutines that open a file and read lines from it. The co_await
connects the suspending and ready state of load_data
to their progress.
open_resource
和read_line
s 可能是打开文件并从中读取行的异步协程。该co_await
的悬浮和准备状态时连接load_data
到他们的进步。
C++ coroutines are much more flexible than this, as they were implemented as a minimal set of language features on top of user-space types. The user-space types effectively define what co_return
co_await
and co_yield
mean-- I've seen people use it to implement monadic optional expressions such that a co_await
on an empty optional automatically propogates the empty state to the outer optional:
C++ 协程比这更灵活,因为它们是作为用户空间类型之上的最小语言功能集实现的。用户空间类型有效地定义了什么co_return
co_await
和co_yield
意味着什么——我已经看到人们使用它来实现一元可选表达式,这样co_await
一个空的可选项会自动将空状态传播到外部可选项:
modified_optional<int> add( modified_optional<int> a, modified_optional<int> b ) {
return (co_await a) + (co_await b);
}
instead of
代替
std::optional<int> add( std::optional<int> a, std::optional<int> b ) {
if (!a) return std::nullopt;
if (!b) return std::nullopt;
return *a + *b;
}
回答by Lothar
A coroutine is like a C function which has multiple return statements and when called a 2nd time does not start execution at the begin of the function but at the first instruction after the previous executed return. This execution location is saved together with all automatic variables that would live on the stack in non coroutine functions.
协程就像一个 C 函数,它有多个 return 语句,第二次调用时不会在函数的开头开始执行,而是在上一次执行的 return 之后的第一条指令处开始执行。该执行位置与所有将在非协程函数中存在于堆栈中的自动变量一起保存。
A previous experimental coroutine implementation from Microsoft did use copied stacks so you could even return from deep nested functions. But this version was rejected by the C++ committee. You can get this implementation for example with the Boosts fiber library.
Microsoft 之前的实验性协程实现确实使用了复制堆栈,因此您甚至可以从深层嵌套函数中返回。但是这个版本被 C++ 委员会拒绝了。例如,您可以使用 Boosts 光纤库获得此实现。
回答by Dr t
coroutines are supposed to be (in C++) functions that are able to "wait" for some other routine to complete and to provide whatever is needed for the suspended, paused, waiting, routine to go on. the feature that is most interesting to C++ folks is that coroutines would ideally take no stack space...C# can already do something like this with await and yield but C++ might have to be rebuilt to get it in.
协程应该是(在 C++ 中)函数,它们能够“等待”其他一些例程完成,并提供挂起、暂停、等待、例程继续所需的任何东西。对 C++ 人来说最有趣的功能是协程理想情况下不占用堆栈空间……C# 已经可以使用 await 和 yield 来做这样的事情,但 C++ 可能需要重新构建才能使用它。
concurrency is heavily focused on the separation of concerns where a concern is a task that the program is supposed to complete. this separation of concerns may be accomplished by a number of means...usually be delegation of some sort. the idea of concurrency is that a number of processes could run independently (separation of concerns) and a 'listener' would direct whatever is produced by those separated concerns to wherever it is supposed to go. this is heavily dependent on some sort of asynchronous management. There are a number of approaches to concurrency including Aspect oriented programming and others. C# has the 'delegate' operator which works quite nicely.
并发非常关注关注点的分离,其中关注点是程序应该完成的任务。这种关注点的分离可以通过多种方式实现……通常是某种委托。并发的想法是许多进程可以独立运行(关注点分离),并且“侦听器”会将这些分离关注点产生的任何内容引导到它应该去的任何地方。这在很大程度上依赖于某种异步管理。有许多并发方法,包括面向方面的编程和其他方法。C# 有“委托”操作符,效果很好。
parallelism sounds like concurrency and may be involved but is actually a physical construct involving many processors arranged in a more or less parallel fashion with software that is able to direct portions of code to different processors where it will be run and the results will be received back synchronously.
并行性听起来像并发性并且可能涉及,但实际上是一种物理结构,涉及许多处理器以或多或少的并行方式排列,软件能够将代码的部分引导到不同的处理器,在那里运行并接收结果同步。