lua的coroutine 协同程序基础

reference:html

http://www.lua.org/manual/5.3/manual.htmlapp

 

2.6 – Coroutines

Lua supports coroutines, also called collaborative multithreading. A coroutine in Lua represents an independent thread of execution. Unlike threads in multithread systems, however, a coroutine only suspends its execution by explicitly calling a yield function.ide

You create a coroutine by calling coroutine.create. Its sole argument is a function that is the main function of the coroutine. The create function only creates a new coroutine and returns a handle to it (an object of type thread); it does not start the coroutine.函数

You execute a coroutine by calling coroutine.resume. When you first call coroutine.resume, passing as its first argument a thread returned bycoroutine.create, the coroutine starts its execution by calling its main function. Extra arguments passed to coroutine.resume are passed as arguments to that function. After the coroutine starts running, it runs until it terminates or yields.this

A coroutine can terminate its execution in two ways: normally, when its main function returns (explicitly or implicitly, after the last instruction); and abnormally, if there is an unprotected error. In case of normal termination, coroutine.resume returns true, plus any values returned by the coroutine main function. In case of errors, coroutine.resume returns false plus an error object.lua

A coroutine yields by calling coroutine.yield. When a coroutine yields, the corresponding coroutine.resume returns immediately, even if the yield happens inside nested function calls (that is, not in the main function, but in a function directly or indirectly called by the main function). In the case of a yield,coroutine.resume also returns true, plus any values passed to coroutine.yield. The next time you resume the same coroutine, it continues its execution from the point where it yielded, with the call to coroutine.yield returning any extra arguments passed to coroutine.resume.spa

Like coroutine.create, the coroutine.wrap function also creates a coroutine, but instead of returning the coroutine itself, it returns a function that, when called, resumes the coroutine. Any arguments passed to this function go as extra arguments to coroutine.resumecoroutine.wrap returns all the values returned by coroutine.resume, except the first one (the boolean error code). Unlike coroutine.resumecoroutine.wrap does not catch errors; any error is propagated to the caller.rest

 

6.2 – Coroutine Manipulation

This library comprises the operations to manipulate coroutines, which come inside the table coroutine. See §2.6 for a general description of coroutines.code

 

coroutine.create (f)

Creates a new coroutine, with body ff must be a function. Returns this new coroutine, an object with type "thread".orm

 

coroutine.isyieldable ()

Returns true when the running coroutine can yield.

A running coroutine is yieldable if it is not the main thread and it is not inside a non-yieldable C function.

 

coroutine.resume (co [, val1, ···])

Starts or continues the execution of coroutine co. The first time you resume a coroutine, it starts running its body. The values val1, ... are passed as the arguments to the body function. If the coroutine has yielded, resume restarts it; the values val1, ... are passed as the results from the yield.

If the coroutine runs without any errors, resume returns true plus any values passed to yield (when the coroutine yields) or any values returned by the body function (when the coroutine terminates). If there is any error, resume returns false plus the error message.

 

coroutine.running ()

Returns the running coroutine plus a boolean, true when the running coroutine is the main one.

 

coroutine.status (co)

Returns the status of coroutine co, as a string: "running", if the coroutine is running (that is, it called status); "suspended", if the coroutine is suspended in a call to yield, or if it has not started running yet; "normal" if the coroutine is active but not running (that is, it has resumed another coroutine); and "dead" if the coroutine has finished its body function, or if it has stopped with an error.

 

coroutine.wrap (f)

Creates a new coroutine, with body ff must be a function. Returns a function that resumes the coroutine each time it is called. Any arguments passed to the function behave as the extra arguments to resume. Returns the same values returned by resume, except the first boolean. In case of error, propagates the error.

 

coroutine.yield (···)

Suspends the execution of the calling coroutine. Any arguments to yield are passed as extra results to resume.

 

Lua的coroutine 跟thread 的概念比较类似,可是也不彻底相同。一个multi-thread的程序,能够同时有多个thread 在运行,可是一个multi-coroutines的程序,同一时间只能有一个coroutine 在运行,并且当前正在运行的coroutine 只有在被显式地要求挂起时,才会挂起。Lua的coroutine 是一个强大的概念,尽管它的几个主要应用都比较复杂。

 

 

 

1. Coroutine 基础

Lua将coroutine相关的全部函数封装在表coroutine 中。create 函数,建立一个coroutine ,以该coroutine 将要运行的函数做为参数,返回类型为thread

co=coroutine.create(function() print("hello") end)
print(co,type(co))

 运行结果:

thread: 0xfd2540	thread

coroutine 有4个不一样的状态:suspended, running, dead, normal。当新create 一个coroutine的时候,它的状态为suspended ,意味着在create 完成后,该coroutine 并无当即运行。咱们能够用函数status 来查看该coroutine 的状态:

co=coroutine.create(function() print("hello") end)
print(co,type(co))
print("status:"..coroutine.status(co))

运行结果:

thread: 0xfd2540	thread
status:suspended

函数coroutine.resume (恢复)运行该coroutine,将其状态从suspended变为running:

co=coroutine.create(function() print("hello") end)
print(co,type(co))
print("status:"..coroutine.status(co))
coroutine.resume(co)

运行结果:

thread: 0xfd2540	thread
status:suspended
hello

在该示例中,coroutine运行,输出一个“hello”就结束了,该coroutine变为dead状态:

co=coroutine.create(function() print("hello") end)
print(co,type(co))
print("status:"..coroutine.status(co))
coroutine.resume(co)
print("status:"..coroutine.status(co))

运行结果:

thread: 0xb16690	thread
status:suspended
hello
status:dead

coroutine的真正强大之处在于它的yield 函数,它能够将正在运行的coroutine 挂起,并能够在适当的时候再从新被唤醒,而后继续运行。下面,咱们先看一个简单的示例:

co1=coroutine.create(function() for i=1, 10 do  print("co1 ", i) coroutine.yield() end print("end") end)


function test() 
  for i=1, 12 do
    co2  = coroutine.resume(co1)
    print(co2,"status:"..coroutine.status(co1))
  end
end
test()

运行结果:

co1 	1
true	status:suspended
co1 	2
true	status:suspended
co1 	3
true	status:suspended
co1 	4
true	status:suspended
co1 	5
true	status:suspended
co1 	6
true	status:suspended
co1 	7
true	status:suspended
co1 	8
true	status:suspended
co1 	9
true	status:suspended
co1 	10
true	status:suspended
end
true	status:dead
false	status:dead

该coroutine每打印一行,都会被挂起,看起来是否是在运行yield 函数的时候被挂起了呢?当咱们用resume 唤醒该coroutine时,该coroutine继续运行,打印出下一行,而且返回值为true。直到最后没有东西打印出来的时候,该coroutine退出循环,变为dead状态(注意最后那里的状态变化)。若是对一个dead状态的coroutine进行resume 操做,coroutine.resume的返回值为false。

注意,resume 是运行在protected mode下。当coroutine内部发生错误时,Lua会将错误信息返回给resume 调用。

 

当一个coroutine A在resume另外一个coroutine B时,A的状态没有变为suspended,咱们不能去resume它;可是它也不是running状态,由于当前正在running的是B。这时A的状态其实就是normal 状态了。

 

Lua的一个颇有用的功能,resume-yield对,能够用来交换数据。下面是4个小示例:

1)main函数中没有yield,调用resume时,多余的参数,都被传递给main函数做为参数,下面的示例,1 2 3分别就是a b c的值了:

co3=coroutine.create(function(a,b,c) print("hello",a,b,c) end)
print(coroutine.resume(co3,1,2,3))
print("status:"..coroutine.status(co3)

运行结果:

hello	1	2	3
true
status:dead

2)main函数中有yield,全部被传递给yield的参数,都被返回。所以resume的返回值,除了标志正确运行的true外,还有传递给yield的参数值:

co4=coroutine.create(function(a,b) print("hello",a,b) coroutine.yield(a+b, a-b)  end)
print(coroutine.resume(co4,10,5))
print("status:"..coroutine.status(co4))

运行结果:

hello	10	5
true	15	5
status:suspended

3)yield也会把多余的参数返回给对应的resume,以下:

co6=coroutine.create(function(a,b) print("hello",a,b,coroutine.yield()) end)
print(coroutine.resume(co6))
print(coroutine.resume(co6,10,5))
print("status:"..coroutine.status(co6))

运行结果:

true
hello	nil	nil	10	5
true
status:dead

其中print(coroutine.resume(co6))返回值为true,这是由于 yield没有返回,print就根本还没运行

另外:

co5=coroutine.create(function(a,b) print("hello",a,b) coroutine.yield() end)
print(coroutine.resume(co5))
print(coroutine.resume(co5,10,5))
print("status:"..coroutine.status(co5))

运行结果:

hello	nil	nil
true
true
status:dead

仔细比较上面两个例子的区别,coroutine.yield()一个是位于print()函数的参数,另一个和print()独立,致使运行结果不一样。

4)当一个coroutine结束的时候,main函数的全部返回值都被返回给resume:

co7=coroutine.create(function() return "finish","ok" end)
print(coroutine.resume(co7))
print("status:"..coroutine.status(co7)

运行结果:

true	finish	ok
status:dead