一、协同程序基础
1.什么是协同程序
协同程序与线程差不多,也就是一条执行序列,拥有自己独立的栈、局部变量和指令指针(即可以保存变量的值和状态),同时又与其他协同程序共享全局变量和其他大部分东西。
与线程的区别是具有多个线程的程序可以同时运行几个线程,而程序任意时刻只能运行一个协同程序,并且协同程序只有被显示地(执行挂起函数)要求挂起才会暂停。
2.四种状态
(1)挂起(suspended):创建时或执行了操作coroutine.yield()
(2)运行(running):执行了coroutine.resume()
(3)死亡(dead):最后一次调用resume时,协同程序的内容已执行完毕,并且已经返回。resume状态为dead的协同程序将返回false及错误消息,不再执行主函数体。
(4)正常(normal):当一个协同程序A唤醒另一个协同程序B时,A就处于一种特殊状态——既不是挂机状态(A不能继续执行),也不是运行状态(B在运行)。
3.相关函数(都存放在全局table coroutine中)
(1)create(fun):用于创建协同程序,只有一个参数,就是一个函数(一般为匿名函数),函数中的代码就是协同程序将要执行的内容。create返回值的类型为thread。创建后的协同程序处于挂起状态。
(2)resume(co,...):启动或再次启动协同程序。参数列表中:co表示将要被启动的协同程序,后面为变长的可选参数。
(3)yield(...):使正在运行的协同程序挂起,之后可以再恢复运行(调用resume)。只能被协同程序的主函数调用(即在协同程序执行的内容中调用)。参数列表为可选的变长参数。
(4)status(co):检查协同程序的当前状态,返回值为四中状态中的一种。参数co为待检查的协同程序。
4.有用机制:通过一对resume-yield交换数据
原理:
(1)所有传递给resume的额外参数(...)都将视为协同程序主函数的参数。如:
co = coroutine.create ( function(a,b) print("co:",a,b) end ) --创建协同程序
coroutine.resume(co,10,20) --> co:10 20 --启动协同程序,其中10,20就是额外参数,传递给了协同程序的主函数(匿名函数)
(2)resume的返回值中包含传递给yield的所有参数。如:
co = coroutine.create( function(a,b)
coroutine.yield(a+b,a-b) --在协同程序的主函数中直接调用yield
i = i+1 --有语法错误
end )
第一次调用resume:print(coroutine.resume(co,20,10)) -->true 30 10 --第一个返回值为true表示协同程序执行完毕或执行到yield之前没有发生错误,后面的返回值就是传递给yield的参数。
第二次调用resume:print(coroutine.resume(co,20,10)) -->false,attempt to perform arithmetic on global ‘i‘ (a nil value) --resume 第一个返回值为true并不能说明协同程序没有错误。
(3)yield的返回值就是对应resume传入的额外参数。如:
co = coroutine.create( function() print("co:",coroutine.yield()) end)
coroutine.resume(co) --> --resume中没有额外参数,则yield不会返回值,协同程序挂起
(4)当协同程序结束时,它的主函数返回的值都将作为resume的返回值。如:
co = coroutine.create( function() return 1,2 end )
print(coroutine.resume(co)) -->true 1 2
二、协同程序的应用
1.生产者-消费者
(1)生产者-消费者一般程序表示:
function producer() --生产者 function consumer() --消费者
while true do while true do
local x = io.read() local x = receive()
send(x) io.write(x,"\n")
end end
end end
问题关键:如何将send和receive匹配起来?
(2)生产者-消费者协同程序实现
function send(x) --协同程序主函数中被调用,因而可以调用yield
coroutine.yield(x) --参数x将作为resume的返回值
end
producer = coroutine.create( function() --在这里可以理解为生产者
while true do
local x = io.read() --输入,这里可理解为生产的东西
send(x)
end
end)
function receive() --在这里可以理解为消费者
local status, value = coroutine.resume(producer) --启动协同程序
return value
end
程序通过调用消费者receive()来唤醒生产者(即协同程序),然后通过将“生产的”内容x作为参数传递给yield,最后作为resume的返回值,赋给value(即消费者得到生产者生产的东西)
实质上利用了一对resume-yield交换数据。