lua table insert_Lua语言:协程

概述

Lua的协程很像系统中的线程,也是一个独立的执行线。不同之处是,线程是并行执行的,而协程是协作式的,即同一时刻只有一个协程在执行,并且只有它自己显式请求挂起时,执行权才会转换。

coroutine库提供了协程的功能:

  • coroutine.create (f): 创建一个执行函数为 f 的新协程,最终返回一个类型为 "thread" 的对象;f 必须是一个 Lua 的函数。创建好协程之后,协程并不马上执行,需要调用coroutine.resume才能执行协程。
  • coroutine.resume (co [, val1, ···]):开始或继续协程,co是协程对象,调用之后执行权就交给co协程,然后等待直到co执行完毕,或是co调用coroutine.yield把自己挂起,执行权才交回来。
  • var1等是传给协程的参数,这个要分两种情况:
  1. 对于刚创建的协程, var1...作为函数f的参数。
  2. 如果是继续执行一个挂起的协程(该协程之前调用了coroutine.yield把自己挂起),var1等参数将作为coroutine.yield的返回值。
  • resume如果执行成功,第1个返回值是true,如果是继续一个挂起的协程,后面还会跟着传入coroutine.yield的那些参数;如果有任何错误发生, resume 返回 false 和错误消息。
  • coroutine.running () : 返回当前执行的协程对象,如果是主协程,后面跟一个true,否则跟一个false。主协程是指Lua的默认执行线,用coroutine.create创建的协程不是主协程。
  • coroutine.yield (···): 挂起当前协程,...是任意参数,这些参数会传递给coroutine.resume的返回值。我们仔细思考coroutine.resume和coroutine.yield,这两个函数构成了协程协作的核心:
  • 先假设有两个协程co1, co2;co1调用coroutine.create创建了co2,然后调用coroutine.resume(co2)启动co2,此时执行权交到co2,co1则一直停在resume那里。
  • co2开始执行,到中间时调用coroutine.yield(1, 2)把自己挂机,并传入1和2;现在执行权又交到co1;而co2则一直停在yield这里。
  • co1从resume返回并继续执行,返回值是true 1 2。co1执行到一半又调用coroutine.resume(co2, "hello", true)把co2唤醒,这时执行又到co2去了,co1停在resume这里。
  • co2得到执行权,从yield返回,返回值是"hello" true,然后自己执行到结束,执行权才又给了co1。
  • 这个过程大概就是这样,很清楚地展示了协程之间的协作。
  • coroutine.status (co): 返回co协程的状态,协程有4个状态:
  • running: 协程正在运行,可以确定就是调用coroutine.status的这个协程。
  • suspended:协程挂起,协程创建后会处于这个状态,或是自己调用yield之后也处于这个状态。
  • dead: 协程执行完毕,或因错误停止,处于死亡状态。
  • normal: 当协程co1调用resume之后,co1停止运行,co1会处于这个状态。

Lua参考手册有一个很好的例子,说明协程之间的执行过程,值得仔细阅读:

function 

最终的输出:

co-body 1       10
foo     2
main    true    4
co-body r
main    true    11      -9
co-body x       y
main    true    10      end
main    false   cannot resume dead coroutine

在PIL(Program In Lua)中,也有一个例子,介绍了如何使用协程把基于事件的异步库转换成同步的写法。这也是一个很好的案例,很值得拿出来说一下。服务器编程中有很多著名的基于事件的异步框架,比如nodejs,大多数API都是异步的方式,通过事件回调返回结果。事实上,这种方式写起来很麻烦,在逻辑越来越复杂的时候,会看到一层嵌一层的回调函数,相当的丑陋。

PIL提供一个简单的事件库:

-- 简单的异步事件库,所有操作都先压入队列,然后在runloop中统一处理

普通的写法是调用lib.readline然后传入回调函数,在回调函数中再调用lib.readline,直到回调中的行为空;接着调用lib.writeline写到流,并在回调函数中继续写,直到把之前读出的行写完为止,最后调用lib.stop结束。lib.runloop是一个消息循环,不断从命令队列中取出命令执行,命令要么是读,要么是写,要么是停止:

local 

这种方式理解起来还是有点绕的,如果读写能像写同步代码那样,那就自然得多了,使用协程可以做到这一点,下面是使用协程对事件库作的一层包装:

local 

最后的逻辑代码是这样的:

run

看起来是不是直观多了,执行过程我们可以简单介绍一下,不过经过上面对协程的描述,自己看大概也能看得明白。

  • 首先调用run函数,run函数创建一个协程并启动它,协程函数会执行我们的代码,这里称之为逻辑函数。后面run会调用lib.runloop()开始循环。现在,整个读写过程就由lib.runloop,和协程中的逻辑函数一起协作完成。
  • 逻辑函数首先执行,在while循环中调用getline取一行;getline会调用lib.readline并传入callback;然后调用coroutine.yield把协程挂起。此时在run中lib.runloop()得到调用开始处理命令。
  • runloop处理好读命令后触发callback,getline中的callback函数得到调用,callback调用coroutine.resume(co, line)让协程继续运行,那么getline中的coroutine.yield就返回,并把line返回。逻辑函数由此得到一行文本,把它加到t中,然后一直while循环重复这个过程,直到line为空。
  • 逻辑函数中的for循环调用putline去写,putline调用lib.writeline往事件库中加入写命令,并调用coroutine.yield()把协程挂起。此时lib.runloop继续执行,写好后回调,putline中的callback得到调用,callback调用coroutine.resume(co)把协程唤醒,又开始for循环,直到文本行写完。
  • 最后逻辑函数返回,run调用lib.stop()结束循环。

真实的工程代码,事件库可能要从网络获取数据,但大体框架是一样的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值