今天咱们先来看下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。就是当数据到达的时候触发个人回调。来减小线程切换带来性能损失。可是这样的坏处也是很大的,主要的坏处就是操做被 “分片” 了,代码写的不是 “一鼓作气” 这种。 而是每次来段数据就要判断 数据够不够处理哇,够处理就处理吧,不够处理就在等等吧。这样代码的可读性很低,其实也不符合人类的习惯。lua
可是协程能够很好解决这个问题,好比 把一个IO操做 写成一个协程。当触发IO操做的时候就自动让出CPU给其余协程。要知道协程的切换很轻的。协程经过这种对异步IO的封装 既保留了性能也保证了代码的 容易编写和可读性。在高IO密集型的程序下很好,可是高CPU密集型的程序下没啥好处。操作系统