【深刻Lua】理解Lua中最強大的特性-coroutine(協程)[轉]

 

###coroutine基礎多線程

Lua所支持的協程全稱被稱做協同式多線程(collaborative multithreading)。Lua爲每一個coroutine提供一個獨立的運行線路。然而和多線程不一樣的地方就是,coroutine只有在顯式調用yield函數後才被掛起,同一時間內只有一個協程正在運行。異步

Lua將它的協程函數都放進了coroutine這個表裏,其中主要的函數以下函數

表格

摘取一段雲風的代碼來詳盡解釋協程的工做機制,在這段代碼中,展現了main thread和協程co之間的交互:oop

<!-- lang: lua -->
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"))

 

下面是運行結果lua

co-body 1 10
foo 2
main true 4
co-body r
main true 11, -9
co-body x y
main false 10 end
main false cannot resume dead coroutine

 

###coroutine和subroutine(子例程)的比較spa

子例程的起始處是惟一的入口點,一旦return就完成了子程序的執行,子程序的一個實例只會運行一次。.net

可是協程不同,協程能夠使用yield來切換到其餘協程,而後再經過resume方法重入**(reenter)到上次調用yield的地方,而且把resume的參數當成返回值傳給了要重入(reenter)**的協程。可是coroutine的狀態都沒有被改變,就像一個能夠屢次返回的subroutine線程

協程的精妙之處就在於協做這一律念,下面咱們用生產者和消費者問題來演示一下協程的基本應用。注意:下面的僞碼是用Lua的思想寫的code

var q = queue()

 

生產者的僞碼協程

loop
    while q is not full
        create product
        add the items to q
    resume to consumer

 

消費者的僞碼

loop
    while q is not empty
        consume product
        remove the items from q
    yield

 

###coroutine的和callback的比較

coroutine常常被用來和callback進行比較,由於一般來講,coroutine和callback能夠實現相同的功能,即異步通訊,好比說下面的這個例子:

<!-- lang: lua -->
bob.walkto(jane)
bob.say("hello")
jane.say("hello")

 

看起來好像是對的,但實際上因爲這幾個動做walkto,say都是須要必定時間才能作完的,因此這段程序若是這樣寫的話,就會致使bob一邊走一邊對jane說hello,而後在同時jane也對bob說hello,致使整個流程十分混亂。

若是使用回調來實現的話,代碼示例以下:

<!-- lang: lua -->
bob.walto(function (  )
    bob.say(function (  )
        jane.say("hello")
    end,"hello")
end, jane)

 

即walto函數回調say函數,say函數再回調下一個say函數,這樣回調看起來十分混亂,讓人沒法一下看出這段代碼的意義.

若是用coroutine的話,能夠使用以下寫法:

<!-- lang: lua -->
co = coroutine.create(function (  )
    local current = coroutine.running
    bob.walto(function (  )
        coroutine.resume(current)
    end, jane)
    coroutine.yield()
    bob.say(function (  )
        coroutine.resume(current)
    end, "hello")
    coroutine.yield()
    jane.say("hello"end)

coroutine.resume(co)

 

在上述代碼中,一旦一個異步函數被調用,協程就會使用coroutine.yield()方法將該協程暫時懸掛起來,在相應的回調函數中加上coroutine.resume(current),使其返回目前正在執行的協程中。

可是,上述代碼中有許多重複的地方,因此能夠經過將封裝的方式將重複代碼封裝起來

<!-- lang: lua -->
function runAsyncFunc( func, ... )
    local current = coroutine.running
    func(function (  )
        coroutine.resume(current)
    end, ...)
    coroutine.yield()
end

coroutine.create(function (  )
    runAsyncFunc(bob.walkto, jane)
    runAsyncFunc(bob.say, "hello")
    jane.say("hello")
end)

coroutine.resume(co)

 

這樣就不須要改變從前的全部回調函數,便可經過攜程的方式解決異步調用的問題,使得代碼的結構很是清晰。

 

 

 

 

轉自:https://my.oschina.net/wangxuanyihaha/blog/186401

相關文章
相關標籤/搜索