原帖:http://blog.csdn.net/soloist/article/details/329381

并发是现实世界的本质特征,而聪明的计算机科学家用来模拟并发的技术手段便是多任务机制。大致上有这么两种多任务技术,一种是抢占式多任务(preemptive multitasking),它让操作系统来决定何时执行哪个任务。另外一种就是协作式多任务(cooperative multitasking),它把决定权交给任务,让它们在自己认为合适的时候自愿放弃执行。这两种多任务方式各有优缺点,前者固有的同步问题使得程序经常有不可预知的行为,而后者则要求任务具备相当的自律精神。

协程(coroutine)技术是一种程序控制机制,早在上世纪60年代就已提出,用它可以很方便地实现协作式多任务。在主流的程序语言(如C++、Java、Pascal等)里我们很少能看到协程的身影,但是现在不少动态脚本语言(Python、Perl)却都提供了协程或与之相似的机制,其中最突出的便是Lua。

Lua语言实现的协程是一种非对称式(asymmetric)协程,或称半对称式(semi-symmetric)协程,又或干脆就叫半协程(semi-coroutine)。这种协程机制之所以被称为非对称的,是因为它提供了两种传递程序控制权的操作:一种是(重)调用协程(通过coroutine.resume);另一种是挂起协程并将程序控制权返回给协程的调用者(通过coroutine.yield)。一个非对称协程可以看做是从属于它的调用者的,二者的关系非常类似于例程(routine)与其调用者之间的关系。既然有非对称式协程,当然也就有对称式(symmetric)协程了,它的特点是只有一种传递程序控制权的操作,即将控制权直接传递给指定的协程。曾经有这么一种说法,对称式和非对称式协程机制的能力并不等价,但事实上很容易根据前者来实现后者。接下来我们就用代码来证明这个事实。

--对称式协程库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提供的非对称式协程不但具有与对称式协程一样强大的能力,而且还能避免程序员滥用机制写出结构混乱的程序。

(zt)Lua的多任务机制——协程(coroutine)的更多相关文章

  1. Lua的多任务机制——协程(coroutine)

    并发是现实世界的本质特征,而聪明的计算机科学家用来模拟并发的技术手段便是多任务机制.大致上有这么两种多任务技术,一种是抢占式多任务(preemptive multitasking),它让操作系统来决定 ...

  2. Lua 协程coroutine

    协程和一般多线程的区别是,一般多线程由系统决定该哪个线程执行,是抢占式的,而协程是由每个线程自己决定自己什么时候不执行,并把执行权主动交给下一个线程. 协程是用户空间线程,操作系统其存在一无所知,所以 ...

  3. Lua的协程(coroutine)

    -------------------------------------------------------------------------------- -- 不携带参数 ---------- ...

  4. qemu核心机制分析-协程coroutine

    关于协程coroutine前面的文章已经介绍过了,本文总结对qemu中coroutine机制的分析,qemu 协程coroutine基于:setcontext函数族以及函数间跳转函数siglongjm ...

  5. 协程coroutine

    协程(coroutine)顾名思义就是“协作的例程”(co-operative routines).跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程 ...

  6. Unity协程(Coroutine)管理类——TaskManager工具分享

    博客分类: Unity3D插件学习,工具分享 源码分析   Unity协程(Coroutine)管理类——TaskManager工具分享 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处 ...

  7. Python并发编程协程(Coroutine)之Gevent

    Gevent官网文档地址:http://www.gevent.org/contents.html 基本概念 我们通常所说的协程Coroutine其实是corporate routine的缩写,直接翻译 ...

  8. Lua的函数调用和协程中,栈的变化情况

    Lua的函数调用和协程中,栈的变化情况 1. lua_call / lua_pcall   对于这两个函数,对栈底是没有影响的--调用的时候,参数会被从栈中移除,当函数返 回的时候,其返回值会从函数处 ...

  9. 并发编程协程(Coroutine)之Gevent

    并发编程协程之Gevent Gevent官网文档地址:http://www.gevent.org/contents.html 基本概念 我们通常所说的协程Coroutine其实是corporate r ...

随机推荐

  1. java学习第六天

    目标 1.  块 2.  GC(了解) 3.  package import 4.  封装 一.块 {}  分类 1.普通块 作用: 组织代码.解决变量的作用域.节约了内存. 在同一个作用域内,不能声 ...

  2. hdu3033 I love sneakers! 分组背包变形

    分组背包要求每一组里面只能选一个,这个题目要求每一组里面至少选一个物品. dp[i, j] 表示前 i 组里面在每组至少放进一个物品的情况下,当花费 j 的时候,所得到的的最大价值.这个状态可以由三个 ...

  3. Python科学画图小结

    Python画图主要用到matplotlib这个库.具体来说是pylab和pyplot这两个子库.这两个库可以满足基本的画图需求,而条形图,散点图等特殊图,下面再单独具体介绍. 首先给出pylab神器 ...

  4. android中ListView_SimpleAdapter

    1.首先看下main_activity.xml.其实里面就放了一个ListView. <LinearLayout xmlns:android="http://schemas.andro ...

  5. Linux系统编程@终端IO

    Linux系统中终端设备种类  终端是一种字符型设备,有多种类型,通常使用tty 来简称各种类型的终端设备.终端特殊设备文件一般有以下几种: 串行端口终端(/dev/ttySn ) ,伪终端(/dev ...

  6. 磁盘检验[转自vbird]

    磁盘检验 由于系统在运行时谁也说不准啥时硬件或者是电源会有问题,所以『死机』可能是难免的情况(不管是硬件还是软件). 现在我们知道文件系统运行时会有硬盘与内存数据异步的状况发生,因此莫名其妙的死机非常 ...

  7. 文件与目录的默认权限与隐藏权限【转vbird】

    一个文件有若干个属性, 包括读写运行(r, w, x)等基本权限,及是否为目录 (d) 与文件 (-) 或者是连结档 (l) 等等的属性! 要修改属性的方法在前面也约略提过了(chgrp, chown ...

  8. 【转】IOS静态库a文件制作流程

    原文网址:http://www.jianshu.com/p/3439598ea61f 1.新建Cocoa Touch Static Library工程 新建工程 2.Xcode的参数设置 " ...

  9. caffe: fuck compile error again : error: a value of type "const float *" cannot be used to initialize an entity of type "float *"

    wangxiao@wangxiao-GTX980:~/Downloads/caffe-master$ make -j8find: `wangxiao/bvlc_alexnet/spl': No suc ...

  10. urllib2

    import urllib2response = urllib2.urlopen("http://www.baidu.com")print response.read() urlo ...