Python 协程是无堆栈的还是有堆栈的?

led*_*ter 14 python coroutine python-asyncio

关于 Python 协程(我主要是指async/await)是无堆栈的还是有堆栈的,我看到了相互矛盾的观点。

一些消息来源称它们是堆栈的:

虽然其他人似乎暗示它们是无堆栈的,例如https://gamelisp.rs/reference/coroutines.html

GameLisp 的协程遵循 Rust、Python、C# 和 C++ 设置的模型。我们的协程是“无堆栈的”

总的来说,我的理解始终是,任何有意义的 async/await 实现都意味着无堆栈协程,而堆栈协程基本上是纤程(用户空间线程,通常或多或少地协作切换),例如 goroutine、Boost.Coroutine,显然是 Lua 中的协程等。

我的理解正确吗?或者 Python 协程在某种程度上与 C++ 中的协程有根本的不同,并且是堆栈式的?或者上述来源的作者是否有不同的意思?

Mis*_*agi 7

TLDR:根据C++ 的定义Python 的协程也是无堆栈的。定义特征是协程的持久状态与堆栈分开存储。

\n
\n

协程是无堆栈的:它们通过返回调用者来暂停执行,并且恢复执行所需的数据与堆栈分开存储。[\xe2\x80\xa6]

\n
\n

在技​​术层面上,Python 需要awaitasync for等来允许子协程挂起其父协程。定期调用的函数不可能挂起其父函数。

\n
async def foo():\n    a = await some_awaitable()  # this may suspend `foo` as well\n    b = some_function()         # this cannot suspend `foo`\n    return a + b\n
Run Code Online (Sandbox Code Playgroud)\n
\n

与无堆栈协程相比,堆栈协程可以从嵌套堆栈框架内挂起。(增强:协程

\n
\n
\n

堆栈和协程

\n

堆栈和协程的交互并不像常规例程那样清晰。例程要么正在执行,要么已完成。这自然映射到向堆栈添加单个执行帧的函数执行;1一旦该帧完成,例程就完成,并且该帧将从堆栈中弹出并丢弃。

\n

相反,协程可以正在执行、暂停或完成。这就提出了一个问题:如何实际处理挂起 \xe2\x80\x93 期间的状态,如何处理局部变量。突出的手段有两个:

\n
    \n
  1. 我们可以将每个部分执行视为常规例程。为了获得协程语义,当这样的例程完成时,我们保护其状态。这允许我们在各个协程之间切换。

    \n
  2. \n
  3. 我们可以将整个执行过程视为一个常规例程。为了获得协程语义,当这样的例程挂起时,我们保护整个堆栈。这允许我们在各个堆栈之间切换。

    \n
  4. \n
\n

解决方案 1. 通常称为stackless \xe2\x80\x93 协程与堆栈分开。这提供了很大的力量,因为每个步骤都是一流的,但它暴露了很多实现细节。通常,我们必须显式模拟嵌套协程执行 \xe2\x80\x93 的堆栈,这就是目的await

\n

解决方案 2. 通常称为stackfull \xe2\x80\x93 协程作为堆栈本身的一部分存在。由于执行是在内部进行的,因此这消除了很多功能,但隐藏了很多实现细节。例程与协程的区别是隐藏的,甚至是无关紧要的 \xe2\x80\x93 任何例程都可能在任何点挂起。

\n

这些定义并不具有普遍性。例如,您可能会发现 1. 被称为 stackfull,因为协程本身保留了自己的挂起堆栈。如有疑问,请比较语义而不是命名。
\n但是,这似乎是最流行的命名方案(请参阅参考资料)。

\n
\n

Python适合用在什么地方?

\n

从技术角度来看,Python 的协程是无堆栈的。协程函数创建一流的协程对象,允许部分执行并在挂起期间在内部存储状态(ascr_frame)。协程是使用显式嵌套的await,并且例程不能执行await任何操作。
\n值得注意的是,Python 本身不支持堆栈协程:常规例程无法暂停执行。2

\n
\n

1帧堆栈的语义并不一定意味着实现中函数执行与内存堆栈上的帧具有 1:1 的关系。相反,可以将“堆栈”视为运行时的抽象描述,可以通过函数执行来定义。实现可能有所不同,但语言的高级语义没有明显的差异。考虑 CPython,它有一个运行 Python 堆栈的 C 堆栈。

\n

2语言语义并不能严格保证不存在 stackfull 协程。有第三方扩展可以添加 stackfull 协程,即greenlet恰如其分的是,这是Stackless Python \xe2\x80\x93引入的,“Stackless”是指解释器对 C 堆栈的使用,而不是协程如何使用堆栈。
\n然而,这是一个独立于Python自身的暂停机制awaityield暂停。

\n

参考:

\n\n