总结一下 《lua程序设计第4版》 24章协程的一部分内容:
目录
API
lua协程所有函数都保存在一个coroutine的表中。
1.coroutine.create
coroutine.create(协程体body) 创建协程,返回一个"thread"类型的协程:
co=coroutine.create(function() print("hi") end)
print(type(co)) ---> thread
2.coroutine.status
协程有四个状态: 挂起suspended、运行running、正常normal、死亡dead。通过coroutine.status来检测协程的状态:
print(coroutine.status(co)) --->suspended
3.couroutine.resume
当一个协程被创建时,会处于挂起状态,使用 couroutine.resume来启动或唤醒:
coroutine.resume(co) -- hi
上例执行完后,打印出一个hi,就会终止,变成死亡状态:
print(coroutine.status(co)) --->dead
4.couroutine.yield
目前为止,协程也就是个复杂的函数调用的方式。而它最强大之处在于yield,该函数可以让一个运行中的协程挂起自己然后在后续恢复运行:
co = coroutine.create(function()
for i = 1, 10 do
print("co",i)
couroutine.yield()
end)
其中,协程进行了一个循环,循环中输出数字打印后挂起,当唤醒协程后,它就会开始执行直到遇到第一个yield:
coroutine.resume(co) --> co 1
coroutine.resume(co) --> co 2
coroutine.resume(co) --> co 3
...
coroutine.resume(co) --> co 10
coroutine.resume(co) --> 不输出任何数据
--当我们再次唤醒它,函数resume将返回false和一条错误信息
print(coroutine.resume(co))
-->false cannot resume dead coroutine
通过resume-yield交换数据
第一个resume函数会把所有额外参数传递给协程的主函数:
co = coroutine.create(function(a,b,c)
print("co",a,b,c+2)
end)
coroutine.resume(co,1,2,3) --> co 1 2 5
而resume的返回值中,第一个返回值为true表示没有错误,之后返回值是yield的参数
co = coroutine.create(function(a,b)
coroutine.yield(a+b,a-b)
end)
print(coroutine.resume(co,20,10)) --> true 30 10
对应的coroutine.yield的返回值是对应的resume的参数
co = coroutine.create (function (x)
print (” co1 ”,x)
print (”co2 ”,coroutine.yield())
end)
coroutine.resume(co,"hi") --co1 hi
coroutine.resume(co,4,5) --co2 4 5
可以看到,第一个resume的参数传给了主函数,第二个resume的参数则变成了yield的返回值。
最后,当一个协程运行结束时,主函数的返回值都将变成对应函数resume的返回值:
co = coroutine.create(function()
return 6,7
end)
print(coroutine.resume(co)) --> true 6 7
总结
1.第一个resume的额外参数会传递给协程的主函数,协程执行结束后会的返回值变成resume的额外返回值
2.第二个resume开始,每一个resume-yield都是对应的,resume的参数是yield的返回值,resume的返回值是yield的参数,一直到最后一个为止,最后一个resume的返回值就是主函数的返回值了
3.综上两点发现,当协程中没有yield时:resume和主函数打交道,resume的额外参数会传递给主函数,resume的返回值也就是主函数执行完后的返回值。但中途有yield函数挂起的话,rsume就会转为和yield打交道:resume的参数是 yield的返回值,resume的返回值是yield的参数
上面分开讲可能比较混乱,下面看一个综合例子:
co = coroutine.create(function(a,b)
print(coroutine.yield(a+b,a-b))
return a+1,b+1
end)
print(coroutine.resume(co,20,10))
print(coroutine.resume(co,15,5))
输出:
true 30 10
15 5
true 21 11
首先create一个协程,然后调用第一个coroutine.resume(co,20,10)去唤醒协程,因其为第一个resume故20 10传递给主函数参数,之后遇到yield挂起协程所以第一个log是属于外部的print的。如果没有yield那么外部的这个resume的返回值就会是协程函数的返回值,但是有了yield故返回值为yield的参数。之后再次唤醒打印出协程函数里的print,返回值为第二个resume的参数,之后外部的print打印的是协程函数的返回值。
5.couroutine.wrap
function example()
local co = coroutine.create(function return 2,3 end)
return function()
local code,result =coroutine.resume(co)
return res
end
end
上面例子将唤醒协程的调用包装在一个函数中,这种使用方法lua提供了一种特殊的函数coroutine.wrap来完成这个功能。和create类似的是它也是创建一个协程,不同点在于函数wrap返回的不是协程本身而是一个函数,当这个函数被调用,协程就会被唤醒。与原先的resume不同,这个函数的返回值中没有错误代码,如上述例子一样,可以用wrap函数简写为:
function example()
return coroutine.wrap(function() return 2,3 end)
end
6.coroutine.running
coroutine.running() 用来访问当前的协程(这段代码在哪个协程调用就会返回哪个协程的地址,主线程也一样)
local co = coroutine.running()
coroutine.resume(co)