原帖:http://blog.csdn.net/soloist/article/details/329381程序員
併發是現實世界的本質特徵,而聰明的計算機科學家用來模擬併發的技術手段即是多任務機制。大體上有這麼兩種多任務技術,一種是搶佔式多任務(preemptive multitasking),它讓操做系統來決定什麼時候執行哪一個任務。另一種就是協做式多任務(cooperative multitasking),它把決定權交給任務,讓它們在本身認爲合適的時候自願放棄執行。這兩種多任務方式各有優缺點,前者固有的同步問題使得程序常常有不可預知的行爲,然後者則要求任務具有至關的自律精神。編程
協程(coroutine)技術是一種程序控制機制,早在上世紀60年代就已提出,用它能夠很方便地實現協做式多任務。在主流的程序語言(如C++、Java、Pascal等)裏咱們不多能看到協程的身影,可是如今很多動態腳本語言(Python、Perl)卻都提供了協程或與之類似的機制,其中最突出的即是Lua。併發
Lua語言實現的協程是一種非對稱式(asymmetric)協程,或稱半對稱式(semi-symmetric)協程,又或乾脆就叫半協程(semi-coroutine)。這種協程機制之因此被稱爲非對稱的,是由於它提供了兩種傳遞程序控制權的操做:一種是(重)調用協程(經過coroutine.resume);另外一種是掛起協程並將程序控制權返回給協程的調用者(經過coroutine.yield)。一個非對稱協程能夠看作是從屬於它的調用者的,兩者的關係很是相似於例程(routine)與其調用者之間的關係。既然有非對稱式協程,固然也就有對稱式(symmetric)協程了,它的特色是隻有一種傳遞程序控制權的操做,即將控制權直接傳遞給指定的協程。曾經有這麼一種說法,對稱式和非對稱式協程機制的能力並不等價,但事實上很容易根據前者來實現後者。接下來咱們就用代碼來證實這個事實。函數
--對稱式協程庫coro.luaoop
--代碼摘自論文"Coroutines in Lua"
--www.inf.puc-rio.br/~roberto/docs/corosblp.pdfui
coro = {}
--coro.main用來標識程序的主函數
coro.main = function() end
-- coro.current變量用來標識擁有控制權的協程,
-- 也即正在運行的當前協程
coro.current = coro.mainlua
-- 建立一個新的協程
function coro.create(f)
return coroutine.wrap(function(val)
return nil,f(val)
end)
endspa
-- 把控制權及指定的數據val傳給協程k
function coro.transfer(k,val)
if coro.current ~= coro.main then
return coroutine.yield(k,val)
else
-- 控制權分派循環
while k do
coro.current = k
if k == coro.main then
return val
end
k,val = k(val)
end
error("coroutine ended without transfering control...")
end
end操作系統
若是暫時還弄不懂上面的程序,不要緊,看看如何使用這個庫後再回頭分析。下面是使用示例:.net
require("coro.lua")
function foo1(n)
print("1: foo1 received value "..n)
n = coro.transfer(foo2,n + 10)
print("2: foo1 received value "..n)
n = coro.transfer(coro.main,n + 10)
print("3: foo1 received value "..n)
coro.transfer(coro.main,n + 10)
end
function foo2(n)
print("1: foo2 received value "..n)
n = coro.transfer(coro.main,n + 10)
print("2: foo2 received value "..n)
coro.transfer(foo1,n + 10)
end
function main()
foo1 = coro.create(foo1)
foo2 = coro.create(foo2)
local n = coro.transfer(foo1,0)
print("1: main received value "..n)
n = coro.transfer(foo2,n + 10)
print("2: main received value "..n)
n = coro.transfer(foo1,n + 10)
print("3: main received value "..n)
end
--把main設爲主函數(協程)
coro.main = main
--將coro.main設爲當前協程
coro.current = coro.main
--開始執行主函數(協程)
coro.main()
上面的示例定義了一個名爲main的主函數,整個程序由它而始,也因它而終。爲何須要一個這樣的主函數呢?上面說了,程序控制權能夠在對稱式協程之間自由地直接傳遞,它們之間無所謂誰從屬於誰的問題,都處於同一個層級,可是應用程序必須有一個開始點,因此咱們定義一個主函數,讓它點燃程序運行的導火線。雖然說各個協程都是平等的,但作爲程序運行原動力的主函數仍然享有特殊的地位(這個世上哪有絕對的平等!),爲此咱們的庫專門用了一個coro.main變量來保存主函數,而且在它執行以前要將它設爲當前協程(雖然上面的main實際只是一個普通函數而非一個真正的協程,但這並沒有太大的關係,之後主函數也被稱爲主協程)。示例運行的結果是:
1: foo1 received value 0
1: foo2 received value 10
1: main received value 20
2: foo2 received value 30
2: foo1 received value 40
2: main received value 50
3: foo1 received value 60
3: main received value 70
協程的執行序列是:main->foo1->foo2->main->foo2->foo1->main->foo1->main。
coro.transfer(k,val)函數中k是將要接收程序控制權的協程,而val是傳遞給k的數據。若是當前協程不是主協程,tansfer(k,val)就簡單地利用coroutine.yield(k,val)將當前協程掛起並傳回兩項數據,即程序控制權的下一站和傳遞給它的數據;不然進入一個控制權分派(dispatch)循環,該循環(重)啓動(resume)k協程,等待它執行到掛起(suspend),並根據此時協程傳回的數據來決定下一個要(重)啓動的協程。從應用示例來看,協程與協程之間彷佛是用transfer直接傳遞控制權的,但實際上這個傳遞仍是經過了主協程。每個在主協程裏被調用(比較coro.current和coro.main是否相同便可判斷出)的transfer都至關於一個協程管理器,它不斷地(重)啓動一個協程,將控制權交出去,而後等那個協程掛起時又將控制權收回,而後再(重)啓動下一個協程...,這個動做不會中止,除非<1>將(重)啓動的協程是主協程;<2>某個協程沒有提供控制權的下一個目的地。很顯然,每一輪分派循環開始時都由主協程把握控制權,在循環過程當中若是控制權的下一站又是主協程的話就意味着這個當初把控制權交出去的主協程transfer操做應該結束了,因此函數直接返回val從而結束這輪循環。對於狀況<2>,由於coro.create(f)建立的協程的體函數(body function)實際是function(val) return nil,f(val) end,因此當函數f的最後一條指令不是transfer時,這個協程終將執行完畢並把nil和函數f的返回值一塊兒返回。若是k是這樣的協程,transfer執行完k,val = k(val)語句後k值就成了nil,這被視爲一個錯誤,由於程序此時無法肯定下一個應該(重)啓動的協程究竟是誰。因此在對稱式模型下,每個協程(固然主協程出外)最後都必須顯式地將控制權傳遞給其它的協程。根據以上分析,應用示例的控制權的分派應爲:
第一輪分派: main->foo1->main->foo2->main->main(結束)
第二輪分派: main->foo2->main->foo1->main->main(結束)
第三輪分派: main->foo1->main->main(結束)
因爲能夠直接指定控制權傳遞的目標,對稱式協程機制擁有極大的自由,但獲得這種自由的代價倒是犧牲程序結構。若是程序稍微複雜一點,那麼即便是很是有經驗的程序員也很難對程序流程有全面而清晰的把握。這很是相似goto語句,它能讓程序跳轉到任何想去的地方,但人們卻很難理解充斥着goto的程序。非對稱式協程具備良好的層次化結構關係,(重)啓動這些協程與調用一個函數很是相似:被(重)啓動的協程獲得控制權開始執行,而後掛起(或結束)並將控制權返回給協程調用者,這與計算機先哲們倡導的結構化編程風格徹底一致。
綜上所述,Lua提供的非對稱式協程不但具備與對稱式協程同樣強大的能力,並且還能避免程序員濫用機制寫出結構混亂的程序。