并发是现实世界的本质特征,而聪明的计算机科学家用来模拟并发的技术手段便是多任务机制。大致上有这么两种多任务技术,一种是抢占式多任务(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提供的非对称式协程不但具有与对称式协程一样强大的能力,并且还能避免程序猿滥用机制写出结构混乱的程序。

转载于:https://www.cnblogs.com/mfrbuaa/p/4295742.html

Lua的多任务机制——协程(coroutine)相关推荐

  1. LUA 协程 Coroutine

    协程 Coroutine 协程(coroutine)并不是 Lua 独有的概念,如果让我用一句话概括,那么大概就是:一种能够在运行途中主动中断,并且能够从中断处恢复运行的特殊函数.(嗯,其实不是函数. ...

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

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

  3. 深入理解lua的协程coroutine

    1. 概述 lua协程和多线程 相同之处:拥有自己独立的桟.局部变量和PC计数器,同时又与其他协程共享全局变量和其他大部分东西 不同之处:一个多线程程序可以同时运行几个线程(并发执行.抢占),而协程却 ...

  4. Lua协程Coroutine是什么

    Lua协程Coroutine是什么 协程和线程不同: 同一时刻,一个多线程程序可以用多个线程同时执行:而协程只能有一个在执行 多线程是抢占式的:而协程是非抢占式的,只有协程显示被挂起,才会被挂起 协程 ...

  5. c++ 协程_理解Python协程(Coroutine)

    由于GIL的存在,导致Python多线程性能甚至比单线程更糟. GIL: 全局解释器锁(英语:Global Interpreter Lock,缩写GIL),是计算机程序设计语言解释器用于同步线程的一种 ...

  6. python3 协程 写法_理解Python的协程(Coroutine)

    由于GIL的存在,导致Python多线程性能甚至比单线程更糟. GIL: 全局解释器锁(英语:Global Interpreter Lock,缩写GIL),是计算机程序设计语言解释器用于同步线程的一种 ...

  7. 理解Python的协程(Coroutine)

    生成器(Generator) yield表达式的使用 生产者和消费者模型 yield from表达式 协程(Coroutine) @asyncio.coroutine async/await 总结 参 ...

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

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

  9. Unity 协程Coroutine综合测试

    Unity 协程Coroutine综合测试 1 using UnityEngine; 2 using System.Collections; 3 using System.Text; 4 5 publ ...

  10. Tornado 异步协程coroutine原理

    协程定义: 协程,又称微线程,纤程.英文名Coroutine. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕 ...

最新文章

  1. flask url构建_如何为生产构建构建Flask-RESTPlus Web服务
  2. JS window对象 Navigator对象 Navigator 对象包含有关浏览器的信息,通常用于检测浏览器与操作系统的版本。...
  3. Java排序算法之——希尔排序
  4. PC机I/O端口分配列表
  5. Item03. 设计模式 Item04. STL
  6. win10更新后开不了机_win7在线更新window10系统
  7. Introduction to Chinese natural language processing
  8. 【设计模式】建造者模式(生成器模式)
  9. linux 设置防火墙ip网段,网络防御-防火墙设置IP网段规则
  10. r语言html爬虫,用R语言三行代码写爬虫
  11. 国庆假期微信大数据报告
  12. Exponetial BackOff(指数退避算法)
  13. 【安全资讯】incaseformat蠕虫病毒大爆发!20s删除用户文件
  14. 一文掌握GSEA,超详细教程!
  15. 如何在linux下查看服务器的型号
  16. Spring Security(15)——权限鉴定结构
  17. IBM@China 还是 China IBM
  18. Ubuntu下使用VSCode编译调试Betaflight飞控
  19. ANSYS流固耦合仿真总结
  20. 国产银河麒麟系统部署Redis、Nginx分享

热门文章

  1. 引用springboot starter 的springboot项目无法引用 springboot starter依赖的项目中的类
  2. python决策树预测模型_「数据挖掘入门系列」数据挖掘模型之分类与预测 - 决策树...
  3. php中绘制长方体,php代码将常见的长方形图片修改为正方形的图片
  4. python正式发布的时间_微软Visual Studio Code 1.44正式发布:加入新Python教程、时间轴视图...
  5. 1.3使用command-line runners
  6. Struts2之类型转换中的错误
  7. 【渝粤教育】广东开放大学 社会学概论 形成性考核 (50)
  8. SVM(1)-概念与理解
  9. MBR、主引导扇区,主分区、扩展分区、逻辑分区,活动分区、引导分区、系统分区、启动分区的区别详解
  10. Baxter实战 (五)安装openNI2,NiTE-2并实现kinect 2实现动作跟随