我們首先介紹一下什麼是協程、然後詳細介紹一下coroutine庫,然後介紹一下協程的簡單用法,最後介紹一下協程的複雜用法。
一、協程是什麼?
(1)線程
首先複習一下多線程。我們都知道線程——Thread。每一個線程都代表一個執行序列。
當我們在程式中建立多線程的時候,看起來,同一時刻多個線程是同時執行的,不過實質上多個線程是并發的,因為隻有一個CPU,是以實質上同一個時刻隻有一個線程在執行。
在一個時間片内執行哪個線程是不确定的,我們可以控制線程的優先級,不過真正的線程排程由CPU的排程決定。
(2)協程
那什麼是協程呢?協程跟線程都代表一個執行序列。不同的是,協程把線程中不确定的地方盡可能的去掉,執行序列間的切換不再由CPU隐藏的進行,而是由程式顯式的進行。
是以,使用協程實作并發,需要多個協程彼此協作。
二、resume和yeild的協作。
resume和yeild的協作是Lua協程的核心。這邊用一幅圖描述一下,有一個大體的印象。對照下面的coroutine庫的詳細解釋和最後的代碼,應該可以搞清楚協程的概念了。
注:這是在非首次resume協程的情況下,resume和yield的互相調用的情況。如果是首次resume協程,那麼resume的參數會直接傳遞給協程函數。

三、coroutine庫詳解
(1)coroutine.create (f)
傳一個函數參數,用來建立協程。傳回一個“thread”對象。
(2)coroutine.isyieldable ()
如果正在運作的協程可以讓出,則傳回真。值得注意的是,隻有主協程(線程)和C函數中是無法讓出的。
(3)coroutine.resume (co [, val1, ···])
這是一個非常重要的函數。用來啟動或再次啟動一個協程,使其由挂起狀态變成運作狀态。
可以這麼說,resume函數相當于在執行協程中的方法。參數Val1...是執行協程co時傳遞給協程的方法。
首次執行協程co時,參數Val1...會傳遞給協程co的函數;
再次執行協程co時,參數Val1...會作為給協程co中上一次yeild的傳回值。
不知道這句話大家了解了沒,這是協程的核心。如果沒了解也不用急,繼續往下看,稍後我會詳細解釋。
resume函數傳回什麼呢?有3種情況:
1)、如果協程co的函數執行完畢,協程正常終止,
resume
傳回 true和函數的傳回值。
2)、如果協程co的函數執行過程中,協程讓出了(調用了yeild()方法),那麼resume傳回true和協程中調用yeild傳入的參數。
3)、如果協程co的函數執行過程中發生錯誤,resume傳回false與錯誤消息。
可以看到resume無論如何都不會導緻程式崩潰。它是在保護模式下執行的。
(4)coroutine.running ()
用來判斷目前執行的協程是不是主線程,如果是,就傳回true。
(5)coroutine.status (co)
傳回一個字元串,表示協程的狀态。有4種狀态:
1)、running。如果在協程的函數中調用status,傳入協程自身的句柄,那麼執行到這裡的時候才會傳回running狀态。
2)、suspended。如果協程還未結束,即自身調用了yeild或還沒開始運作,那麼就是suspended狀态。
3)、normal。如果協程Aresume協程B時,協程A處于的狀态為normal。在協程B的執行過程中,協程A就一直處于normal狀态。因為它這時候既不是挂起狀态、也不是運作狀态。
4)、dead。如果一個協程發生錯誤結束,或正常終止。那麼就處于dead狀态。如果這時候對它調用resume,将傳回false和錯誤消息。
(6)coroutine.wrap (f)
wrap()也是用來建立協程的。隻不過這個協程的句柄是隐藏的。跟create()的差別在于:
1)、wrap()傳回的是一個函數,每次調用這個函數相當于調用coroutine.resume()。
2)、調用這個函數相當于在執行resume()函數。
3)、調用這個函數時傳入的參數,就相當于在調用resume時傳入的除協程的句柄外的其他參數。
4)、調用這個函數時,跟resume不同的是,它并不是在保護模式下執行的,若執行崩潰會直接向外抛出。
(7)coroutine.yield (···)
使正在執行的函數挂起。
傳遞給yeild的參數會作為resume的額外傳回值。
同時,如果對該協程不是第一次執行resume,resume函數傳入的參數将會作為yield的傳回值。
四、例子進階。
(1)、例子1:簡單實用resume、yield,如下:
coco = coroutine.create(function (a,b)
print("resume args:"..a..","..b)
yreturn = coroutine.yield()
print ("yreturn :"..yreturn)
end)
coroutine.resume(coco,0,1)
coroutine.resume(coco,21)
輸出:
resume args:0,1
yreturn :21
(2)、例子2:簡單使用wrap,如下:
coco2 = coroutine.wrap(function (a,b)
print("resume args:"..a..","..b)
yreturn = coroutine.yield()
print ("yreturn :"..yreturn)
end)
print(type(coco2))
coco2(0,1)
coco2(21)
function
resume args:0,1
yreturn :21
很明顯,wrap的使用更友善。
(3)、如果還沒有足夠的了解,且看我放大招,看這個例子:
function status()
print("co1's status :"..coroutine.status(co1).." ,co2's status: "..coroutine.status(co2))
end
co1 = coroutine.create(function ( a )
print("arg is :"..a)
status()
local stat,rere = coroutine.resume(co2,"2")
print("resume's return is "..rere)
status()
local stat2,rere2 = coroutine.resume(co2,"4")
print("resume's return is "..rere2)
local arg = coroutine.yield("6")
end)
co2 = coroutine.create(function ( a )
print("arg is :"..a)
status()
local rey = coroutine.yield("3")
print("yeild's return is " .. rey)
status()
coroutine.yield("5")
end)
--主線程執行co1,傳入字元串“main thread arg”
stat,mainre = coroutine.resume(co1,"1")
status()
print("last return is "..mainre)
用一個函數status()輸出2個協程的狀态,最後輸出如下:
arg is :1
co1's status :running ,co2's status: suspended
arg is :2
co1's status :normal ,co2's status: running
resume's return is 3
co1's status :running ,co2's status: suspended
yeild's return is 4
co1's status :normal ,co2's status: running
resume's return is 5
co1's status :suspended ,co2's status: suspended
last return is 6
(4)、最後附一個雲風在Lua5.3參考手冊中給出的例子:
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
foo 2
main true 4
co-body r
main true 11 -9
co-body x y
main true 10 end
main false cannot resume dead coroutine
/**
* ————————如果覺得本博文還行,别忘了推薦一下哦,謝謝!
* 作者:錢書康
* 歡迎轉載,請保留此段聲明。
* 出處:http://www.cnblogs.com/zrtqsk/
*/