协程是非抢断式的多线程方式,看上去像多线程,其实如同名字说的—协程,在协调多段程序的运行并且各自的代码段是有自己独立的变量的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
看完这个例子之后,其实可以看出来这两个线程其实一直在配合做事情。当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之下也没有关系。