Lua协程

协程是非抢断式的多线程方式,看上去像多线程,其实如同名字说的—协程,在协调多段程序的运行并且各自的代码段是有自己独立的变量的cache。本文主要通过实例将协程的处理讲清楚。
resume和yield是一个很好的桥梁,将代码段之间方便的传输参数。

接口函数:

函数 参数 说明
coroutine.create f 创建协程
coroutine.resume co [, val1, ···] 开启或者继续一个协程
coroutine.running 返回是否协程在运行,或者返回nil当被主线程调用时
coroutine.status co 返回协程状态:running,suspended,normal,dead
coroutine.wrap co 原生态创建协程
coroutine.yield 挂起正在执行的

coroutine.wrap有点特别,这个是我摘抄文档中的解释:
类似 coroutine.create , coroutine.wrap 这个函数也将创建一个 coroutine , 但是它并不返回 coroutine 本身,而是返回一个函数取而代之。一旦你调用这个返回函数,就会切入 coroutine 运行。 所有传入这个函数的参数等同于传入 coroutine.resume 的参数。 coroutine.wrap 会返回所有应该由除第一个(错误代码的那个布尔量) 之外的由 coroutine.resume 返回的值。 和 coroutine.resume 不同, coroutine.wrap 不捕获任何错误; 所有的错误都应该由调用者自己传递。

官方的一个实例:

 function foo (a)
   print("foo", a)
   return coroutine.yield(2*a)
 end

 co = coroutine.create(function (a,b)
       print("co-body", a, b)
       local r = foo(a+1)
       print("co-body", r)
       local r, s = coroutine.yield(a+b, a-b)
       print("co-body", r, s)
       return b, "end"
 end)

 print("main", coroutine.resume(co, 1, 10))
 print("main", coroutine.resume(co, "r"))
 print("main", coroutine.resume(co, "x", "y"))
 print("main", coroutine.resume(co, "x", "y"))

输出结果:

>lua -e "io.stdout:setvbuf 'no'" "test.lua" 
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

调用顺序:

Created with Raphaël 2.1.0 主线程 主线程 协程 协程 resume(co, 1, 10) 主线程挂起,唤起协程 foo (1+1) coroutine.yield(2*2) 协程挂起,主线程唤醒 return resume()->主线程获得2*2返回值 coroutine.resume(co, "r")主线程挂起,唤起协程 local r = foo(a+1)触发resume("r")参数赋值给local r coroutine.yield(a+b, a-b)协程挂起,主线程唤醒,将会1+10,1-10 print("main", coroutine.resume(co, "r"))将yield的参数输出 coroutine.resume(co, "x", "y")主线程挂起,唤起协程 local r, s = coroutine.yield(a+b, a-b)主线程中输入的"x","y"变量将会被赋值给r,s 由于协程已经执行全部的流程最终将会把流程交回给主线程,协程将会变成dead态 print("main", coroutine.resume(co, "x", "y"))将会触发协程函数返回值的打印 X,协程已经不再工作状态,所以造成程序直接返回,服务器不能唤起

看完这个例子之后,其实可以看出来这两个线程其实一直在配合做事情。当resume被调用的时候,主线程就暂停了活动,将运行的权限交给了coroutine来做事。当coroutine yield的时候,将会直接触发自己的coroutine暂停,激活主线程的resume的return,接着做事。所以理论上对一个coroutine的调用,函数中有几个yield,主线程的代码中需要些多少个resume函数,否则如果主线程只对coroutine做一次resume。但是这个也不会造成太多的内存问题,协程对象被释放掉之后也将不会存在内存泄露
下面就举个例子来做一下测试说明此问题。


function first_phase(a)
    -- coroutine first phase
    return coroutine.yield( a + 1 )
end


collectgarbage('collect')
print(string.format("after full garbage-collection cycle, memory: %d k",collectgarbage('count')))

for i = 1, 100 do
    local co = coroutine.create( function ( a, b )
        local l_a, l_b = a, b
        local m1 = first_phase(a)
        -- coroutine second phase
        local m2 = coroutine.yield( m1 + 1 )
        -- coroutine third phase
        return input * b
    end)

    first_return = coroutine.resume( co, 10, 8 )
    -- without to call resume let coroutine to finish

    if i == 99 then
        collectgarbage('collect')
        print(string.format("after full garbage-collection cycle, memory: %d k",collectgarbage('count')))
    end
end

collectgarbage('collect')
print(string.format("after full garbage-collection cycle, memory: %d k",collectgarbage('count')))

输出:

after full garbage-collection cycle, memory: 20 k
after full garbage-collection cycle, memory: 21 k
after full garbage-collection cycle, memory: 20 k

这个说明了问题,就是其实在coroutine的Up-value的数据都是被保存在coroutine对象中。一旦释放掉这些对象,其Up-value数据也将会被回收,即使是这个coroutine状态还是在suspended之下也没有关系。

你可能感兴趣的:(lua,协程)