Lua的協程和協程庫詳解

咱們首先介紹一下什麼是協程、而後詳細介紹一下coroutine庫,而後介紹一下協程的簡單用法,最後介紹一下協程的複雜用法。多線程

1、協程是什麼?併發

  (1)線程函數

  首先複習一下多線程。咱們都知道線程——Thread。每個線程都表明一個執行序列。spa

  當咱們在程序中建立多線程的時候,看起來,同一時刻多個線程是同時執行的,不過實質上多個線程是併發的,由於只有一個CPU,因此實質上同一個時刻只有一個線程在執行。線程

  在一個時間片內執行哪一個線程是不肯定的,咱們能夠控制線程的優先級,不過真正的線程調度由CPU的調度決定。3d

  (2)協程code

  那什麼是協程呢?協程跟線程都表明一個執行序列。不一樣的是,協程把線程中不肯定的地方儘量的去掉,執行序列間的切換再也不由CPU隱藏的進行,而是由程序顯式的進行。orm

  因此,使用協程實現併發,須要多個協程彼此協做。協程

 

2、resume和yeild的協做。對象

  resume和yeild的協做是Lua協程的核心。這邊用一幅圖描述一下,有一個大致的印象。對照下面的coroutine庫的詳細解釋和最後的代碼,應該能夠搞清楚協程的概念了。

  注:這是在非首次resume協程的狀況下,resume和yield的互相調用的狀況。若是是首次resume協程,那麼resume的參數會直接傳遞給協程函數。

 

 

3、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的返回值。

 

4、例子進階。

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