Lua的多任务机制——协程(coroutine)
并发是现实世界的本质特征,而聪明的计算机科学家用来模拟并发的技术手段便是多任务机制。大致上有这么两种多任务技术,一种是抢占式多任务(preemptive multitasking),它让操作系统来决定何时运行哪个任务。第二种就是协作式多任务(cooperative multitasking),它把决定权交给任务,让它们在自己觉得合适的时候自愿放弃运行。这两种多任务方式各有优缺点,前者固有的同步问题使得程序常常有不可预知的行为,而后者则要求任务具备相当的自律精神。
协程(coroutine)技术是一种程序控制机制,早在上世纪60年代就已提出,用它能够非常方便地实现协作式多任务。在主流的程序语言(如C++、Java、Pascal等)里我们非常少能看到协程的身影,可是如今不少动态脚本语言(Python、Perl)却都提供了协程或与之相似的机制,当中最突出的便是Lua。
Lua语言实现的协程是一种非对称式(asymmetric)协程,或称半对称式(semi-symmetric)协程,又或干脆就叫半协程(semi-coroutine)。这种协程机制之所以被称为非对称的,是由于它提供了两种传递程序控制权的操作:一种是(重)调用协程(通过coroutine.resume);还有一种是挂起协程并将程序控制权返回给协程的调用者(通过coroutine.yield)。一个非对称协程能够看做是从属于它的调用者的,二者的关系非常相似于例程(routine)与其调用者之间的关系。既然有非对称式协程,当然也就有对称式(symmetric)协程了,它的特点是仅仅有一种传递程序控制权的操作,即将控制权直接传递给指定的协程。以前有这么一种说法,对称式和非对称式协程机制的能力并不等价,但其实非常easy依据前者来实现后者。接下来我们就用代码来证明这个事实。
--对称式协程库coro.lua
--代码摘自论文"Coroutines in Lua"
--www.inf.puc-rio.br/~roberto/docs/corosblp.pdf
coro = {}
--coro.main用来标识程序的主函数
coro.main = function() end
-- coro.current变量用来标识拥有控制权的协程,
-- 也即正在运行的当前协程
coro.current = coro.main
-- 创建一个新的协程
function coro.create(f)
return coroutine.wrap(function(val)
return nil,f(val)
end)
end
-- 把控制权及指定的数据val传给协程k
function coro.transfer(k,val)
if coro.current ~= coro.main then
return coroutine.yield(k,val)
else
-- 控制权分派循环
while k do
coro.current = k
if k == coro.main then
return val
end
k,val = k(val)
end
error("coroutine ended without transfering control...")
end
end
假设临时还弄不懂上面的程序,没关系,看看怎样使用这个库后再回头分析。以下是使用演示样例:
require("coro.lua")
function foo1(n)
print("1: foo1 received value "..n)
n = coro.transfer(foo2,n + 10)
print("2: foo1 received value "..n)
n = coro.transfer(coro.main,n + 10)
print("3: foo1 received value "..n)
coro.transfer(coro.main,n + 10)
end
function foo2(n)
print("1: foo2 received value "..n)
n = coro.transfer(coro.main,n + 10)
print("2: foo2 received value "..n)
coro.transfer(foo1,n + 10)
end
function main()
foo1 = coro.create(foo1)
foo2 = coro.create(foo2)
local n = coro.transfer(foo1,0)
print("1: main received value "..n)
n = coro.transfer(foo2,n + 10)
print("2: main received value "..n)
n = coro.transfer(foo1,n + 10)
print("3: main received value "..n)
end
--把main设为主函数(协程)
coro.main = main
--将coro.main设为当前协程
coro.current = coro.main
--開始运行主函数(协程)
coro.main()
上面的演示样例定义了一个名为main的主函数,整个程序由它而始,也因它而终。为什么须要一个这种主函数呢?上面说了,程序控制权能够在对称式协程之间自由地直接传递,它们之间无所谓谁从属于谁的问题,都处于同一个层级,可是应用程序必须有一个開始点,所以我们定义一个主函数,让它点燃程序运行的导火线。虽说各个协程都是平等的,但做为程序运行原动力的主函数仍然享有特殊的地位(这个世上哪有绝对的平等!),为此我们的库专门用了一个coro.main变量来保存主函数,并且在它运行之前要将它设为当前协程(尽管上面的main实际仅仅是一个普通函数而非一个真正的协程,但这并无太大的关系,以后主函数也被称为主协程)。演示样例运行的结果是:
1: foo1 received value 0
1: foo2 received value 10
1: main received value 20
2: foo2 received value 30
2: foo1 received value 40
2: main received value 50
3: foo1 received value 60
3: main received value 70
协程的运行序列是:main->foo1->foo2->main->foo2->foo1->main->foo1->main。
coro.transfer(k,val)函数中k是将要接收程序控制权的协程,而val是传递给k的数据。假设当前协程不是主协程,tansfer(k,val)就简单地利用coroutine.yield(k,val)将当前协程挂起并传回两项数据,即程序控制权的下一站和传递给它的数据;否则进入一个控制权分派(dispatch)循环,该循环(重)启动(resume)k协程,等待它运行到挂起(suspend),并依据此时协程传回的数据来决定下一个要(重)启动的协程。从应用演示样例来看,协程与协程之间似乎是用transfer直接传递控制权的,但实际上这个传递还是通过了主协程。每个在主协程里被调用(比較coro.current和coro.main是否同样就可以推断出)的transfer都相当于一个协程管理器,它不断地(重)启动一个协程,将控制权交出去,然后等那个协程挂起时又将控制权收回,然后再(重)启动下一个协程...,这个动作不会停止,除非<1>将(重)启动的协程是主协程;<2>某个协程没有提供控制权的下一个目的地。非常显然,每一轮分派循环開始时都由主协程把握控制权,在循环过程中假设控制权的下一站又是主协程的话就意味着这个当初把控制权交出去的主协程transfer操作应该结束了,所以函数直接返回val从而结束这轮循环。对于情况<2>,由于coro.create(f)创建的协程的体函数(body function)实际是function(val) return nil,f(val) end,所以当函数f的最后一条指令不是transfer时,这个协程终将运行完成并把nil和函数f的返回值一起返回。假设k是这种协程,transfer运行完k,val = k(val)语句后k值就成了nil,这被视为一个错误,由于程序此时没法确定下一个应该(重)启动的协程究竟是谁。所以在对称式模型下,每个协程(当然主协程出外)最后都必须显式地将控制权传递给其它的协程。依据以上分析,应用演示样例的控制权的分派应为:
第一轮分派: main->foo1->main->foo2->main->main(结束)
第二轮分派: main->foo2->main->foo1->main->main(结束)
第三轮分派: main->foo1->main->main(结束)
由于能够直接指定控制权传递的目标,对称式协程机制拥有极大的自由,但得到这种自由的代价却是牺牲程序结构。假设程序略微复杂一点,那么即使是非常有经验的程序猿也非常难对程序流程有全面而清晰的把握。这非常相似goto语句,它能让程序跳转到不论什么想去的地方,但人们却非常难理解充斥着goto的程序。非对称式协程具有良好的层次化结构关系,(重)启动这些协程与调用一个函数非常相似:被(重)启动的协程得到控制权開始运行,然后挂起(或结束)并将控制权返回给协程调用者,这与计算机先哲们倡导的结构化编程风格全然一致。
综上所述,Lua提供的非对称式协程不但具有与对称式协程一样强大的能力,并且还能避免程序猿滥用机制写出结构混乱的程序。
Lua的多任务机制——协程(coroutine)的更多相关文章
- (zt)Lua的多任务机制——协程(coroutine)
原帖:http://blog.csdn.net/soloist/article/details/329381 并发是现实世界的本质特征,而聪明的计算机科学家用来模拟并发的技术手段便是多任务机制.大致上 ...
- Lua 协程coroutine
协程和一般多线程的区别是,一般多线程由系统决定该哪个线程执行,是抢占式的,而协程是由每个线程自己决定自己什么时候不执行,并把执行权主动交给下一个线程. 协程是用户空间线程,操作系统其存在一无所知,所以 ...
- Lua的协程(coroutine)
-------------------------------------------------------------------------------- -- 不携带参数 ---------- ...
- qemu核心机制分析-协程coroutine
关于协程coroutine前面的文章已经介绍过了,本文总结对qemu中coroutine机制的分析,qemu 协程coroutine基于:setcontext函数族以及函数间跳转函数siglongjm ...
- 协程coroutine
协程(coroutine)顾名思义就是“协作的例程”(co-operative routines).跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程 ...
- Unity协程(Coroutine)管理类——TaskManager工具分享
博客分类: Unity3D插件学习,工具分享 源码分析 Unity协程(Coroutine)管理类——TaskManager工具分享 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处 ...
- Python并发编程协程(Coroutine)之Gevent
Gevent官网文档地址:http://www.gevent.org/contents.html 基本概念 我们通常所说的协程Coroutine其实是corporate routine的缩写,直接翻译 ...
- Lua的函数调用和协程中,栈的变化情况
Lua的函数调用和协程中,栈的变化情况 1. lua_call / lua_pcall 对于这两个函数,对栈底是没有影响的--调用的时候,参数会被从栈中移除,当函数返 回的时候,其返回值会从函数处 ...
- 并发编程协程(Coroutine)之Gevent
并发编程协程之Gevent Gevent官网文档地址:http://www.gevent.org/contents.html 基本概念 我们通常所说的协程Coroutine其实是corporate r ...
随机推荐
- EasyUI DataGrid和Pagination
连接一台EasyUI项目驱动学习 DataGrid数据表格及Pagination分页一起介绍 一.通过<table>标记创建DataGrid,嵌套<th>标签定义列表 < ...
- cocos2D(五岁以下儿童)---- CCNode
本将主要介绍下CCNode这个类.CCNode是全部节点的基类,当中包含我们经常使用的CCScene(场景).CCLayer(图层).CCSprite(精灵)等.它是一个不可以可视化显示的抽象类,仅仅 ...
- 设置状态栏样式Demo
达到的效果: 色有黑色变为了白色 //设置状态条的样式 - (UIStatusBarStyle)preferredStatusBarStyle { returnUIStatusBarStyleLigh ...
- BZOJ 2588 Count on a tree (COT) 是持久的段树
标题效果:两棵树之间的首次查询k大点的权利. 思维:树木覆盖树,事实上,它是正常的树木覆盖了持久段树. 由于使用权值段树可以寻求区间k大,然后应用到持久段树思想,间隔可以做减法.详见代码. CODE: ...
- How to pause the game in Uniy3D
static float timeScale; Description The scale at which the time is passing. This can be used for slo ...
- C++11的一些功能
.断言是将一个须要为真的表达式放在语句中,在debug模式下检查一些逻辑错误的參数.C++中使用assert须要使用<assert.h>或者<cassert>头文件.有函数定义 ...
- Cocos2dx-3.1.1 冒险01----> 文件夹结构、新项目project创建并执行
windows开发环境:window7.vs2012.python2.7.6 Cocos2d-x 3.1.1的完整文件夹例如以下:比起曾经的2.x的版本号来说分类更规范了 watermark/2/te ...
- A*寻路算法lua实现
前言:并在相当长的时间没有写blog该,我觉得有点"颓废"该,最近认识到各种同行,也刚刚大学毕业,我认为他们是优秀的.认识到与自己的间隙,有点自愧不如.我没有写blog当然,部分原 ...
- vs2015管理github代码
- 并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法(转)
在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列(先进先出).Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQ ...