明天咱们先来看下 lua 手册上一个协程实例:
手册实例:
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"))
执行后果:
co-body 1 10 -- 协程 co 的第 7 行,此时 resume() 传入的参数是赋值给了函数的
foo 2 -- 在第 8 行外面调用了函数 foo(),执行到第 2 行的打印
main true 4 -- 因为函数 foo() 的第 3 行 yield() 执行后挂起,参数是 4,作为第 15 行的 resume() 的第二个返回值,最终打印了进去,到此,第 15 行执行结束
co-body r -- 第 16 行 resume() 再次唤醒协程 co,接着上次 yield() 的中央继续执行,参数“r" 被赋值给上次 yield() 的返回值,在第 9 行打印进去
main true 11 -9 -- 在第 10 行 yiled() 后再次挂起协程 co,并返回,此时参数 a 和 b 还是第一次 resume() 时的参数,1,10,所以 yield() 两个参数别离为 11,-9,作为 resum() 的第二个返回值,最终被打印进去,到此,第 16 行执行结束
co-body x y -- 第 17 行 resume() 再次唤醒协程 co, 传入的参数“x”,“y”被赋值给上次的 yield() 函数的返回值,即赋值给第 10 行的 r,s, 在第 11 行被打印进去
main true 10 end -- 协程 co 在第 12 行返回,留神此时参数 b 依然是第一次 resume() 时的参数 2,值为 10,至此协程 co 执行完结,变为 dead 状态,最终在第 17 行打印进去
main false cannot resume dead coroutine -- 第 18 行尝试再次 resume() 协程 co,因为协程 co 曾经为 dead 状态,所以间接返回并报错
下面这个实例很好的展现了 coroutine.yield 和 coroutine.resume 之间的相互作用。协程在生产者和消费者问题上应该用也比拟宽泛,咱们来看看上面这个例子。
生产者与消费者:
coConsume = coroutine.create(function ()
while true do
local stutas, msg = coroutine.resume(coProducer)
print('receive msg :', msg)
coroutine.resume(coProducer, string.len(msg))
end
end
)
coProducer = coroutine.create(function ()
while true do
local msg = io.read()
local len = coroutine.yield(msg)
print('he tell me he has recved data len is', len)
coroutine.yield()
end
end
)
coroutine.resume(coConsume)
运行后果:
hello -- 从键盘输入
receive msg : hello
he tell me he has recved data len is 5
下面这个实例有两个协程,一个是生产协程次要是从屏幕上接管输出的数据,另外一个是消费者,解决承受到的信息将其打印。
总结:
最初援用知乎上的一段话作为协程学习的一个小结。
在 IO 密集型的程序中因为 IO 操作远远小于 CPU 的操作,所以往往须要 CPU 去等 IO 操作。同步 IO 下零碎须要切换线程,让操作系统能够在 IO 过程中执行其余的货色。这样尽管代码是合乎人类的思维习惯然而因为大量的线程切换带来了大量的性能的节约,尤其是 IO 密集型的程序。
所以人们创造了异步 IO。就是当数据达到的时候触发我的回调。来缩小线程切换带来性能损失。然而这样的害处也是很大的,次要的害处就是操作被“分片”了,代码写的不是“零打碎敲”这种。而是每次来段数据就要判断 数据够不够解决哇,够解决就解决吧,不够解决就在等等吧。这样代码的可读性很低,其实也不合乎人类的习惯。
然而协程能够很好解决这个问题,比方 把一个 IO 操作 写成一个协程。当触发 IO 操作的时候就主动让出 CPU 给其余协程。要晓得协程的切换很轻的。协程通过这种对异步 IO 的封装 既保留了性能也保障了代码的 容易编写和可读性。在高 IO 密集型的程序下很好,然而高 CPU 密集型的程序下没啥益处。