您的位置:首页 > 编程语言 > Lua

Lua 协程随笔

2013-08-31 19:01 232 查看
协同程序

coroutine  [,kəuru:'ti:n]

1、概念
     协同程序类似多线程情况下的线程,也就是一条执行序列,拥有自己独立的栈,局部变量和指令指针,同时又与其它协同程序共享全局变量和其它大部分东西。线程程与协同程序的主要区别在于,一个具有多线程的程序可以同时运行几个线程,而协同程序却需要彼此协作地运行。就是说,一个具有多个协同程序的程序在任何时刻只能运行一个协同程序,并且正在运行的协同程序只会在其显示地挂起时,它的执行才会暂停。

     lua虽然是单线程,串行执行的脚步语言,但利用协程还是可以实现一些异步处理的特性。协同程序可以简化看出是一个复杂的调用函数的方式(协程内部就是一个函数方法),它的核心是 yield 函数,yield函数可以将正在运行的代码(协同内部的函数)挂起,处理其他事务,处理完成再次唤醒该协程,
     Lua语言实现的协程是一种非对称式(asymmetric)协程,或称半对称式(semi-symmetric)协程,又或干脆就叫半协程(semi-coroutine)。这种协程机制之所以被称为非对称的,是因为它提供了两种传递程序控制权的操作:一种是(重)调用协程(通过coroutine.resume);另一种是挂起协程并将程序控制权返回给协程的调用者(通过coroutine.yield)。一个非对称协程可以看做是从属于它的调用者的,二者的关系非常类似于例程(routine)与其调用者之间的关系。既然有非对称式协程,当然也就有对称式(symmetric)协程了,它的特点是只有一种传递程序控制权的操作,即将控制权直接传递给指定的协程。曾经有这么一种说法,对称式和非对称式协程机制的能力并不等价,但事实上很容易根据前者来实现后者。

2、特性

     优点

由于是假的线程,所以切换线程的开销极小,且不用线程调度所以可以更充分利用cpu。
每个coroutine有自己私有的stack及局部变量,相当于一个小的执行环境。
同一时间只有一个coroutine在执行,无需对全局变量加锁,但因为共享全局变量所以在被挂起期间是可能被其他协程修改。
顺序可控,完全由程序控制执行的顺序。而通常的多线程一旦启动,它的运行时序是没法预测的,因此通常会给测试所有的情况带来困难。

     缺点
          因为lua本质还是是单线程,所以一个协程阻塞会堵住所有的协程,也就是说协程内部如果也被阻塞的话就会导致整个应用阻塞(协同并非真正意义的多线程)。

3、使用

coro = coroutine.create(function(...)
-- do xxx
print(coroutine.status(coro)); -- running
print("run data" .. arg[1]);
local status, result = coroutine.yield(os.time());
print("status = " .. status);
print("result = " .. result);
end)
print(type(coro));   -- thread
-- 协程的三个状态  挂起(suspended) 运行(running) 停止(dead)
print(coroutine.status(coro));   -- suspended
local result, startTime = coroutine.resume(coro, 1);     -- startTime保存yield的第一个参数 'os.time()'
print(result, startTime);
print(coroutine.status(coro));   -- suspended
local result = coroutine.resume(coro, 1, "success");

     
     在执行到yield之后,代码跳转到上一次resume代码的后一条代码执行
     再次调用resume,代码就跳转到上一次yield代码的后一条代码执行。

     一般来说,resume方法在主线程中调用;而yield则是在coroutine内调用,包括coroutine内部调用的函数内部.
     在coroutine中调用resume没有什么问题,但这样是没有什么意义的
     在main线程不能调用yield,会导致 “lua: attempt to yield across metamethod/C-call boundary”的错误。

4、应用场景
      感觉最大的应用场景主要是可以替代回调机制,当需要异步执行等待返回结果的的时候,先启动一个协程,待发出请求后将协程挂起去处理其他事务。异步结果完成后再返回请求的结果到该协程,唤醒该协程继续执行,看上去就像是"发送请求","请求返回","处理返回结果",能在很大程度上提高代码可读性和减少代码量。
      另外一个应用是复杂的迭代器,比如二叉树的前序遍历:
function preOrder(node)
if node then
preOrder(node.left);
coroutine.yield(node.key, node.value);
preOrder(node.right);
end
end
function createIter(node) do
return coroutine.wrap( function()
preOrder(node);
return nil;
end;
end
for k,v in createIter(tree) do
print(k, v);
end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: