多线程和协程
多线程是抢占式多任务(preemptive multitasking),每个子线程由操作系统来决定何时执行,由于执行时间不可预知所以多线程需要使用同步技术来避免某些问题。在单核计算机中,同一时刻只有一个线程允许运行,而在多核计算机中,同一时刻可以有多个线程同时运行(比如8核CPU就可以最多让8个线程同时运行)。
协程是协作式多任务(cooperative multitasking),它把决定权交给任务,让它们在自己认为合适的时候自愿放弃执行。协程不是多线程,无论是几核CPU,同一时刻都只会有一个协程在运行。
有意思的是主流的程序语言(如C++、Java、Pascal等)里我们很少能看到协程的身影,但是现在不少动态脚本语言(Lua、Python、Perl)却都提供了协程或与之相似的机制。
Lua本身不支持多线程的,不同线程共享一个State会出问题,但是支持协程。
Lua协程
官方文档:http://www.lua.org/manual/5.2/manual.html#6.2
最简单的示例
下面我们来看一个简单的示例:
1 --创建一个协程对象, 传入的参数为该协程运行的主函数 2 co = coroutine.create( 3 function() 4 print("step 1") 5 coroutine.yield() 6 print("step 2") 7 coroutine.yield() 8 print("step 3") 9 end 10 ) 11 12 --运行协程 13 print(coroutine.status(co)) 14 coroutine.resume(co) 15 print("main thread code 1") 16 --运行协程 17 print(coroutine.status(co)) 18 coroutine.resume(co) 19 print("main thread code 2") 20 --运行协程 21 print(coroutine.status(co)) 22 coroutine.resume(co) 23 print("main thread code 2") 24 25 print(coroutine.status(co))
我们先看下运行的结果:
1 suspended 2 step 1 3 main thread code 1 4 suspended 5 step 2 6 main thread code 2 7 suspended 8 step 3 9 main thread code 2 10 dead
我们可以发现,在协程中遇到coroutine.yield()时,该协程的运行会被暂停这时主线程会继续运行,直到调用coroutine.resume方法时对应的协程才会继续运行。当协程的方法运行完毕时该协程也就结束了,state为dead。
带参数和返回的协程
协程是可以带有参数和返回值的,如下:
1 --创建一个协程对象, 传入的参数为该协程运行的主函数 2 co = coroutine.create( 3 function(a, b, c) 4 local i = 0 5 print("add", "add", "i:", i) 6 i = 10 7 coroutine.yield(a + b, b + c) 8 print("add", "sub", "i:", i) 9 i = 100 10 coroutine.yield(a + b, b - c) 11 print("sub", "sub", "i:", i) 12 i = 1000 13 coroutine.yield(a - b, b - c) 14 print("i:", i) 15 end 16 ) 17 18 --运行协程 19 state, result1, result2 = coroutine.resume(co, 10, 5, 3) 20 print(state, result1, result2) 21 --运行协程 22 state, result1, result2 = coroutine.resume(co, 100, 50, 30) 23 print(state, result1, result2) 24 --运行协程 25 state, result1, result2 = coroutine.resume(co, 1000, 500, 300) 26 print(state, result1, result2) 27 28 coroutine.resume(co)
运行结果如下:
1 add add i: 0 2 true 15 8 3 add sub i: 10 4 true 15 2 5 sub sub i: 100 6 true 5 2 7 i: 1000
我们看看协程参数和返回值要注意的一点地方:
- 参数只有第一次调用时传入有效,后面调用resume传入的参数会被忽略;
- 返回值的第一个值为调用是否成功的一个布尔值,如果成功则后跟返回的参数,如果失败则后跟报错的信息;