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
相關文章
相關標籤/搜索