什么是协程?它们与并发性有什么关系?


当前回答

来自Python Coroutine:

Python协程的执行可以在许多地方暂停和恢复 点(见协程)。在协程函数体内,等待 异步标识符成为保留关键字;等待表达式, Async for和Async with只能在协程函数中使用 的身体。

From Coroutines (c++ 20)

协程是一种可以挂起要恢复的执行的函数 以后。协程是无堆栈的:它们通过返回来暂停执行 给打电话的人。这允许执行顺序代码 异步(例如,不显式地处理非阻塞I/O 回调),并且还支持惰性计算无限上的算法 序列和其他用途。

与他人的答案比较:

在我看来,后面的恢复部分是一个核心的区别,就像@Twinkle一样。 虽然文件的很多方面还在完善中,但是这一部分和大部分的答案是相似的,除了@南晓

另一方面,协程是协作的:在任何给定的时间,一个 带有协程的程序只运行其中一个协程,并且 这个正在运行的协程仅在显式执行时才会暂停执行 请求被暂停。

因为引用自Program in Lua,可能是语言相关(目前不熟悉Lua),不是所有文档都只提到一个部分。

与concurrent的关系: 协程(c++ 20)中有一个“执行”部分。太长了,不能在这里引用。 除了细节,还有几个状态。

When a coroutine begins execution  
When a coroutine reaches a suspension point  
When a coroutine reaches the co_return statement  
If the coroutine ends with an uncaught exception  
When the coroutine state is destroyed either because it terminated via co_return or uncaught exception, or because it was destroyed via its handle 

正如@Adam Arold在@user217714的回答下的评论。并发性。 但它与多线程不同。 从std::线程

Threads allow multiple functions to execute concurrently. Threads begin execution immediately upon construction of the associated thread object (pending any OS scheduling delays), starting at the top-level function provided as a constructor argument. The return value of the top-level function is ignored and if it terminates by throwing an exception, std::terminate is called. The top-level function may communicate its return value or an exception to the caller via std::promise or by modifying shared variables (which may require synchronization, see std::mutex and std::atomic)

因为它是并发的,它就像多线程一样工作,特别是当等待是不可避免的(从操作系统的角度来看),这也是为什么它令人困惑的原因。

其他回答

我将详述@user21714的答案。协程是独立的执行路径,不能同时运行。它们依赖于一个控制器(例如python控制器库)来处理这些路径之间的切换。但是为了实现这一点,协同程序本身需要调用yield或类似的结构,以允许它们的执行暂停。

相反,线程运行在独立的计算资源上,并且彼此并行。由于它们位于不同的资源上,因此不需要调用yield来允许其他执行路径继续进行。

您可以通过启动一个多线程程序(例如jvm应用程序)来看到这种效果,其中所有八个核心i7超线程核心都被利用了:您可能会在Activity Monitor或Top中看到797%的利用率。相反,当运行一个典型的python程序(即使是带有协程或python线程的程序)时,利用率最高将达到100%。例如,一台机器超线程。

协程类似于子例程/线程。 区别在于,一旦调用方调用了子例程/线程,它将永远不会返回到调用方函数。 但是协程可以在执行几段代码后返回到调用方,允许调用方执行一些自己的代码,并返回到它停止执行的协程点,并从那里继续。 ie。协程有多个入口点和出口点

来自Python Coroutine:

Python协程的执行可以在许多地方暂停和恢复 点(见协程)。在协程函数体内,等待 异步标识符成为保留关键字;等待表达式, Async for和Async with只能在协程函数中使用 的身体。

From Coroutines (c++ 20)

协程是一种可以挂起要恢复的执行的函数 以后。协程是无堆栈的:它们通过返回来暂停执行 给打电话的人。这允许执行顺序代码 异步(例如,不显式地处理非阻塞I/O 回调),并且还支持惰性计算无限上的算法 序列和其他用途。

与他人的答案比较:

在我看来,后面的恢复部分是一个核心的区别,就像@Twinkle一样。 虽然文件的很多方面还在完善中,但是这一部分和大部分的答案是相似的,除了@南晓

另一方面,协程是协作的:在任何给定的时间,一个 带有协程的程序只运行其中一个协程,并且 这个正在运行的协程仅在显式执行时才会暂停执行 请求被暂停。

因为引用自Program in Lua,可能是语言相关(目前不熟悉Lua),不是所有文档都只提到一个部分。

与concurrent的关系: 协程(c++ 20)中有一个“执行”部分。太长了,不能在这里引用。 除了细节,还有几个状态。

When a coroutine begins execution  
When a coroutine reaches a suspension point  
When a coroutine reaches the co_return statement  
If the coroutine ends with an uncaught exception  
When the coroutine state is destroyed either because it terminated via co_return or uncaught exception, or because it was destroyed via its handle 

正如@Adam Arold在@user217714的回答下的评论。并发性。 但它与多线程不同。 从std::线程

Threads allow multiple functions to execute concurrently. Threads begin execution immediately upon construction of the associated thread object (pending any OS scheduling delays), starting at the top-level function provided as a constructor argument. The return value of the top-level function is ignored and if it terminates by throwing an exception, std::terminate is called. The top-level function may communicate its return value or an exception to the caller via std::promise or by modifying shared variables (which may require synchronization, see std::mutex and std::atomic)

因为它是并发的,它就像多线程一样工作,特别是当等待是不可避免的(从操作系统的角度来看),这也是为什么它令人困惑的原因。

If you are still confused, here is a very simple way of understanding a co-routine. First off, what is a routine? In a lay man's term, a routine is something that we do again and again (for example, your morning routine). Similarly. in programming languages, a routine is a piece of code that we use again and again, e.g., a function. Now, if you look at the general characteristic of a function or routine (note: I am cautiously using these two terms interchangeably), it takes some inputs and hogs the CPU threads for as long as the function needs to output the result. Meaning, functions or routines are blocking calls in you code. However, a co-routine is a special kind of routine that can co-exist (the "co" part of the word co-routine comes from this) with other routines at the same time and we can make this happen in programming languages with the help of asynchronous programming. In Asynchronous programming, when one co-routine is waiting for something to happen (e.g., disk io), the other co-routine will start working and when this co-routine is in a waiting state the other co-routine will be active ultimately reducing the waiting time of our code.

如果你理解了上面的内容,让我们看看如何在Python中创建协程函数。可以像下面那样定义协程函数-

async def my_coroutine_function():
    return 123

你可以通过在协同程序-前面添加await来调用上面的协同程序

my_result = await my_coroutine_function()

最后,

当你正在看电视节目,广告一出现,你就拿起手机给朋友发短信——你刚刚做的就是异步编程。当你的电视节目(一个合作程序)处于等待状态时,你继续前进,让你的另一个合作程序(给你的朋友发短信)处于激活状态。

在Lua编程中,“协程”部分:

A coroutine is similar to a thread (in the sense of multithreading): it is a line of execution, with its own stack, its own local variables, and its own instruction pointer; but it shares global variables and mostly anything else with other coroutines. The main difference between threads and coroutines is that, conceptually (or literally, in a multiprocessor machine), a program with threads runs several threads in parallel. Coroutines, on the other hand, are collaborative: at any given time, a program with coroutines is running only one of its coroutines, and this running coroutine suspends its execution only when it explicitly requests to be suspended.

所以关键是:协程是“协作的”。即使在多核系统中,也只有一个协程在任何给定时间运行(但多个线程可以并行运行)。协程之间存在不可抢占性,运行中的协程必须显式放弃执行。

关于“并发性”,你可以参考Rob Pike的幻灯片:

并发是独立执行计算的组合。

所以在协程A的执行过程中,它把控制权传递给了协程B。经过一段时间后,协程B又把控制权传递给了协程A。由于协程之间存在依赖关系,它们必须串联运行,所以这两个协程不是并发的。