lua中的协同程序

来源:互联网 发布:csol大刀优化参数 编辑:程序博客网 时间:2024/06/09 16:20

1.协同程序相关的几个函数

lua中的协同程序类似于线程,拥有自己的独立资源(独立栈,自己的指针和局部变量等),也和其他协同程序共享全局的资源(比如同属于同一个lua的luastate 的多个协同程序可以共享该luastate中的全局变量),但也和线程有不一样的地方,当一个程序拥有多个线程时,他们是并发执行的,是一种抢占式的关系,然而一个具有多个协同程序的程序,同一时刻只能有一个协同程序在运行,是一种彼此合作的关系,从而减少同步带来的一些问题。
lua中的协同主要有以下几个函数来操作:coroutine.create(para),coroutine.status(para),coroutine.resume(para),coroutine.yield().我们用coroutine.create(para)来创建一个协同,并打印一下创建出来的结果:
function coroutineTest()print("i am a coroutine program")endprint(coroutine.create(coroutineTest))
显示的是我们创建的协同的地址.那我们程序中打印的那句话哪里去了呢?原来是因为协同创建之后是处在一个挂起的状态,我们可以通过coroutine.status()来查看一个协同的状态(挂起,运行,死亡,正常),我们可以使用coroutine.resume()来恢复其运行:
function coroutineTest()print("i am a coroutine program")endlocal co =coroutine.create(coroutineTest)print(coroutine.status(co))coroutine.resume(co)

我们打印了创建一个协同时的状态和协同程序结束后的状态。当一个协同程序里面唤醒另一个协同程序时,那前一个协同程序就处在一种特殊的状态,“”正常“”状态.
其实,协同程序的强大在于他的yield机制,他可以让正在运行的协同程序挂起,必要的时候再恢复其运行,而且,一对yield和resume还可以互换数据,下面我们直接上代码:
function co()for i=1,10 doprint(i)coroutine.yield()endendlocal coroutineTest = coroutine.create(co)--执行一次后挂起coroutine.resume(coroutineTest)--接着上次执行后挂起coroutine.resume(coroutineTest)print("*************************************************")--resume如果没有错误会返回true和yield中传入的所有参数co1 = coroutine.create(function(a,b) coroutine.yield(a+b,a-b) end )print(coroutine.resume(co1,1,2))print("*************************************************")--yield也会返回resume传给他的额外参数co2 = coroutine.create(function() print(coroutine.yield())end)coroutine.resume(co2) --不打印任何东西coroutine.resume(co2,1,2) print("*************************************************")--resume会返回主函数所有返回的值co3 = coroutine.create(function(a,b)coroutine.yield(a+b) return 6 end)print(coroutine.resume(co3,2,3))print(coroutine.resume(co3))
下面是运行之后的结果:

2.协同程序中的经典案例

1.生产者消费者问题.
大体意思就是说生产者函数不停的生产,消费者函数不停的消费生产者的产品,为了不浪费资源,我们需要将这两个函数匹配起来,那么到底以谁作为循环条件的开始就成了要讨论的问题,协同程序可以很好的解决这类问题,我们直接lua程序设计一书中提供的代码:
--消费者function consumer()while true dolocal x=receive()io.write(x,"\n")endendfunction receive()local status,value=coroutine.resume(producer)return valueendfunction send(x)--用到前面的yield 和 resume互传参数coroutine.yield(x)end--生产者producer = coroutine.create(function()while true do local x = io.read()send(x)endend)


这种设计是以消费者来启动的,消费者需要值时唤醒生产者去生产,生产者生产后被挂起,我们还可以在当中对生产的值进行包装,然后供消费者消费,依然看书中提供的代码:
--消费者function consumer(prod)while true do local x =receive(prod) --获取新的值io.write(x,"\n")endendfunction receive(prod)local status,value=coroutine.resume(prod)return valueendfunction send(x)--用到前面的yield 和 resume互传参数coroutine.yield(x)end--生产者function producer()return coroutine.create(function()while true do local x = io.read()send(x)endend)endfunction filter(prod)return coroutine.create(function()for line = 1,math.huge dolocal x = receive(prod)x=string.format("消费".."%5d行  %s",line,x)send(x)endend)end--运行上述程序consumer(filter(producer()))


运行上述程序的结果为:

如果出现乱码,请用记事本打开你的lua文件保存为asic格式在再试。
2.用协同实现迭代器
我在之前的博客中讲到过利用closure和for循环分别实现lua中的迭代器,但是不可避免的他们都需要保存上一次成功迭代的一些信息,而学习了协同之后,我们可以利用协同来实现不用保存迭代状态信息的迭代器。来看下面的实例:
function factory()local co = coroutine.create(function()for i=1,10 do coroutine.yield(i)endend)return function() local code ,res = coroutine.resume(co)return resendendfor p in factory() doprint("item=".. p)end
注意,这里仅仅是为了演示协同实现的迭代器不用保存两次迭代之间的状态信息,其实我们完全可以用一个循环来实现该实例,但是当遇到一些很复杂的迭代时,使用协同实现的迭代器是很方便的,下面贴上运行结果:

对于上面的迭代器的实现,我们把对一个协同的创建和唤醒放在一个函数里,由于这样的做法在lua里很常见,lua提供了一个函数来简化流程:coroutine.wrap(),该函数会创建一个协同程序,但是不返回协同本身,而是返回一个函数,每次调用该函数就唤醒协同程序,于是我们可以把上面的factory改写成以下形式,运行后结果与上面是一样的:
function factory()return coroutine.wrap(function()for i=1,10 do coroutine.yield(i) end end)endfor p in factory() doprint("item=".. p)end
当然我们还可以使用协同实现http远程并发下载资源,就类似于多线程一样,但是少了多线程同步带来的一些问题,代价就是cpu资源会耗费的多,lua程序设计一书中有详细的例子,这里就不再实现了。
3.小结:
通过上面的讲述,我们认识到lua中的协同是一个很有用的东西,在我们的不同的处理下可以完成很多任务,上面讲了一点协同的基础,如果想更深刻的认识他,需要我们在平时多留意用到协同的场合,博主水平有限,如有不正确的地方欢迎大家批评指出,交流使人进步。




原创粉丝点击