Lua协同的基础

协同程序

协同程序(coroutine)与多线程情况下的线程比较类似:有自己的堆栈,自己的局部变量,有自己的指令指针,但是和其他协同程序共享全局变量等很多信息。线程和协同程序的主要不同在于:在多处理器情况下,从概念上来讲多线程程序同时运行多个线程;而协同程序是通过协作来完成,在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起。

协同是非常强大的功能,但是用起来也很复杂。如果你第一次阅读本章时不理解本章中的例子请不要担心,你可以继续阅读本书的其他部分然后再回过头来阅读本章。

协同的基础

Lua 通过 table 提供了所有的协同函数,create 函数创建一个新的协同程序,create只有一个参数:协同程序将要运行的代码封装而成的函数,返回值为 thread 类型的值表示创建了一个新的协同程序。通常情况下,create 的参数是一个匿名函数:

co = coroutine.create(function () 
 print("hi") 
end) 
print(co) --> thread: 0x8071d98 

协同有三个状态:挂起态、运行态、停止态。当我们创建一个协同程序时他开始的状态为挂起态,也就是说我们创建协同程序的时候不会自动运行,可以使用 status 函数检查协同的状态:

print(coroutine.status(co)) --> suspended 
函数 coroutine.resume 可以使程序由挂起状态变为运行态:
coroutine.resume(co) --> hi 
这个例子中,协同体仅仅打印出"hi"之后便进入终止状态:
print(coroutine.status(co)) --> dead 

当目前为止,协同看起来只是一种复杂的调用函数的方式,真正的强大之处体现在yield 函数,它可以将正在运行的代码挂起,看一个例子:

co = coroutine.create(function () 
for i=1,10 do
 print("co", i) 
 coroutine.yield() 
end 
end) 
现在重新执行这个协同程序,程序将在第一个 yield 处被挂起:
coroutine.resume(co) --> co 1 
print(coroutine.status(co)) --> suspended 

从协同的观点看:使用函数 yield 可以使程序挂起,当我们激活被挂起的程序时,yield返回并继续程序的执行直到再次遇到 yield 或者程序结束。

coroutine.resume(co) --> co 2 
coroutine.resume(co) --> co 3 
... 
coroutine.resume(co) --> co 10 
coroutine.resume(co) -- prints nothing 
上面最后一次调用的时候,协同体已经结束,因此协同程序处于终止状态。如果我们仍然企图激活他,resume 将返回 false 和错误信息。
print(coroutine.resume(co)) 
 --> false cannot resume dead coroutine 
注意:resume 运行在保护模式下,因此如果协同内部存在错误 Lua 并不会抛出错误而是将错误返回给 resume 函数。

Lua 中一对 resume-yield 可以相互交换数据。

下面第一个例子 resume,没有相应的 yield,resume 把额外的参数传递给协同的主程序。

co = coroutine.create(function (a,b,c) 
 print("co", a,b,c) 
end) 
coroutine.resume(co, 1, 2, 3) --> co 1 2 3 
第二个例子,resume 返回除了 true 以外的其他部分将作为参数传递给相应的 yield 
co = coroutine.create(function (a,b) 
 coroutine.yield(a + b, a - b) 
end) 
print(coroutine.resume(co, 20, 10)) --> true 30 10 
对称性,yield 返回的额外的参数也将会传递给 resume。
co = coroutine.create (function () 
 print("co", coroutine.yield()) 
end) 
coroutine.resume(co) 
coroutine.resume(co, 4, 5) --> co 4 5 

最后一个例子,当协同代码结束时主函数返回的值都会传给相应的 resume:

co = coroutine.create(function () 
return 6, 7 
end) 
print(coroutine.resume(co)) --> true 6 7 

我们很少在同一个协同程序中使用这几种特性,但每一种都有其用处。

现在已经了解了一些协同的内容,在我们继续学习以前,先要澄清两个概念:Lua提供的这种协同我们称为不对称的协同,就是说挂起一个正在执行的协同的函数与使一个被挂起的协同再次执行的函数是不同的,有些语言提供对称的协同,这种情况下,由执行到挂起之间状态转换的函数是相同的。

有人称不对称的协同为半协同,另一些人使用同样的术语表示真正的协同,严格意义上的协同不论在什么地方只要它不是在其他的辅助代码内部的时候都可以并且只能使执行挂起,不论什么时候在其控制栈内都不会有不可决定的调用。

(However, other people use the same term semi-coroutine to denote a restricted implementation of coroutines, where a coroutine can only suspend its execution when it is not inside any auxiliary function, that is, when it has no pending calls in its control stack.)。只有半协同程序的主体中才可以 yield,python 中的产生器(generator)就是这种类型的半协同的例子。

与对称的协同和不对称协同的区别不同的是,协同与产生器的区别更大。产生器相对比较简单,他不能完成真正的协同所能完成的一些任务。我们熟练使用不对称的协同之后,可以利用不对称的协同实现比较优越的对称协同。

发布了257 篇原创文章 · 获赞 152 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_39885372/article/details/104337904
今日推荐