lua 5.0的实现(翻译)6

    从5.0版本开始,Lua实现了“非对称协程”,也叫做“半对称协程”或者“半协程”。这些协程由Lua标准库:create、resume和yield提供支持。create函数接收一个"main"函数,并使用该函数创建一个新的协程。他返回一个类型为thread的值表示该协程。(和所有Lua的值一样,协程是一阶[first-class]的值)resume函数通过调用他的主函数来启动一个给定协程的执行过程。yield函数挂起正在运行协程的执行过程,并返回控制给那个调用resume该协程的调用者。

    理论上,每个协程都有他自己的栈。(实际上,每个协程有2个栈,这个我们将在第七节讨论,但是我们将他们作为一个抽象的栈进行讨论)Lua中的协程是栈依赖的,从这个意义上说,我们可以从任意嵌套的调用中挂起一个协程。解释器只是简单地将整个栈存储留到后来使用,并继续运行另外一个栈。一个程序只要愿意,就能够重启任何一个挂起地协程。垃圾收集器能够回收那些不再被协程访问的栈。

    栈依赖和一阶状态的混合使得协程相当于一次性完成运行,正如Lua中所实现的那样。同样的,他们允许开发者能够实现多种高级控制机制,比如多线程间的互操作,生成器、对称式协程,回退等等。

    Lua中,协程实现的关键点是,解释器无法使用他内部的c栈来实现解释后代码的调用。(Python社区称一个遵循该限制的解释器为无栈解释器)当一个主解释器循环执行一个调用操作时,他在栈上创建一个新的槽,调整指针,并根据调用函数的指令继续循环。这不是巧合,恰恰是真实CPU执行一个函数调用的执行过程。

    然而,当一个解释器恢复执行一个协程时,他将嵌套调用主解释器的函数。这个新的调用主要是为了执行恢复后的协程,使用协程的栈,完成执行并返回。当然新的循环退出时,他将返回到上次解释器的调用,将放弃该协程栈中任何挂起的调用。换句话说,Lua使用c的栈来跟踪任何时刻,活动的协程的栈。每次退出都返回到上次解释器的循环点,该点调用了相应的resume操作。

    在某些语言中,实现协程的苦难来源于如何捕获外部句柄变量。因为,运行在协程中函数可能是由另外一个协程创建的,他可能执行另外一个不同栈的变量。这个导致一些作者所说的“刺球”结构。我们在第五节讨论的“扁平闭包”将避免这个问题。

 

 

6 Threads and Coroutines
Since version 5.0, Lua implements asymmetric coroutines (also called semisymmetric
coroutines or semi-coroutines) [7]. Those coroutines are supported
by three functions from the Lua standard library: create, resume, and yield.
(These functions live in the coroutine namespace.) The create function receives
a /main" function and creates a new coroutine with that function. It
returns a value of type thread that represents that coroutine. (Like all values in
Lua, coroutines are rst-class values.) The resume function (re)starts the execution
of a given coroutine, calling its main function. The yield function suspends
the execution of the running coroutine and returns the control to the call that
resumed that coroutine.
Conceptually, each coroutine has its own stack. (Concretely, each coroutine
has two stacks, as we shall discuss in Section 7, but we can consider them as
a single abstract stack.) Coroutines in Lua are stackful, in the sense that we
can suspend a coroutine from inside any number of nested calls. The interpreter
simply puts aside the entire stack for later use and continues running on another
stack. A program can restart any suspended coroutine at will. The garbage
collector collects stacks whose coroutines are no longer accessible.
The combination of stackfulness and rst-class status makes coroutines, as
implemented in Lua, is equivalent to one-shot continuations. As such, they allow
programmers to implement several advanced control mechanisms, such as cooperative
multithreading, generators, symmetric coroutines, backtracking, etc. [7].

 

A key point in the implementation of coroutines in Lua is that the interpreter
cannot use its internal C stack to implement calls in the interpreted code. (The
Python community calls an interpreter that follows that restriction a stackless
interpreter [23].) When the main interpreter loop executes a call operation, it
creates a new slot in the stack, adjusts several pointers, and continues the loop
with the instructions of the called function. Similarly, a return operation removes
the top stack slot, adjusts pointers, and continues the loop with the instructions
of the calling function. Not by coincidence, that is exactly what a real CPU does
to perform function calls.
When the interpreter executes a resume, however, it does a recursive call to
the main interpreter function. This new invocation is responsible for executing
the resumed coroutine, using the coroutine stack to perform calls and returns.
When this new loop executes an yield, it returns to the previous interpreter
invocation, leaving the coroutine stack with any pending calls. In other words,
Lua uses the C stack to keep track of the stack of active coroutines at any given
time. Each yield returns to the previous interpreter loop, which is the one that
called the corresponding resume.
A source of diculties in the implementation of coroutines in some languages
is how to handle references to outer local variables. Because a function running
in a coroutine may have been created in another coroutine, it may refer to
variables in a dierent stack. This leads to what some authors call a cactus
structure [18]. The use of
at closures, as we discussed in Section 5, avoids this
problem altogether.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值