協(xié)程具有協(xié)同的性質(zhì),它允許兩個(gè)或多個(gè)方法以某種可控的方式協(xié)同工作。在任何一個(gè)時(shí)刻,都只有一個(gè)協(xié)程在運(yùn)行,只有當(dāng)正在運(yùn)行的協(xié)程主動(dòng)掛起時(shí)它的執(zhí)行才會(huì)被掛起(暫停)。
上面的定義可能看上去比較模糊。接下來讓我講得很清楚一點(diǎn),假設(shè)我們有兩個(gè)方法,一個(gè)是主程序方法,另一個(gè)是一個(gè)協(xié)程。當(dāng)我們使用 resume 函數(shù)調(diào)用一個(gè)協(xié)程時(shí),協(xié)程才開始執(zhí)行。當(dāng)在協(xié)程調(diào)用 yield 函數(shù)時(shí),協(xié)程掛起執(zhí)行。再次調(diào)用 resume 函數(shù)時(shí),協(xié)程再從上次掛起的地方繼續(xù)執(zhí)行。這個(gè)過程一直持續(xù)到協(xié)程執(zhí)行結(jié)束為止。
下面的表中列出 Lua 語言為支持協(xié)程而提供的所有函數(shù)以及它們的用法。
S.N. | 方法和功能 |
---|---|
1 | coroutine.create(f):用函數(shù) f 創(chuàng)建一個(gè)協(xié)程,返回 thread 類型對(duì)象。 |
2 | coroutine.resume(co[,val1,...]): 傳入?yún)?shù)(可選),重新執(zhí)行協(xié)程 co。此函數(shù)返回執(zhí)行狀態(tài),也可以返回其它值。 |
3 | coroutine.running():返回正在運(yùn)行的協(xié)程,如果在主線程中調(diào)用此函數(shù)則返回 nil。 |
4 | coroutine.status(co):返回指定協(xié)程的狀態(tài),狀態(tài)值允許為:正在運(yùn)行(running),正常(normal),掛起(suspended),結(jié)束(dead)。 |
5 | coroutine.wrap(f):與前面 coroutine.create 一樣,coroutine.wrap 函數(shù)也創(chuàng)建一個(gè)協(xié)程,與前者返回協(xié)程本身不同,后者返回一個(gè)函數(shù)。當(dāng)調(diào)用該函數(shù)時(shí),重新執(zhí)行協(xié)程。 |
6 | coroutine.yield(...):掛起正在執(zhí)行的協(xié)程。為此函數(shù)傳入的參數(shù)值作為執(zhí)行協(xié)程函數(shù) resume 的額外返回值(默認(rèn)會(huì)返回協(xié)程執(zhí)行狀態(tài))。 |
讓我們通過下面的例子來理解一下協(xié)程這個(gè)概念。
co = coroutine.create(function (value1,value2)
local tempvar3 =10
print("coroutine section 1", value1, value2, tempvar3)
local tempvar1 = coroutine.yield(value1+1,value2+1)
tempvar3 = tempvar3 + value1
print("coroutine section 2",tempvar1 ,tempvar2, tempvar3)
local tempvar1, tempvar2= coroutine.yield(value1+value2, value1-value2)
tempvar3 = tempvar3 + value1
print("coroutine section 3",tempvar1,tempvar2, tempvar3)
return value2, "end"
end)
print("main", coroutine.resume(co, 3, 2))
print("main", coroutine.resume(co, 12,14))
print("main", coroutine.resume(co, 5, 6))
print("main", coroutine.resume(co, 10, 20))
執(zhí)行上面的程序,我們可以得到如下的輸出結(jié)果:
coroutine section 1 3 2 10
main true 4 3
coroutine section 2 12 nil 13
main true 5 1
coroutine section 3 5 6 16
main true 2 end
main false cannot resume dead coroutine
和前面說到的一樣,在例子中我們使用 resume 函數(shù)繼續(xù)執(zhí)行協(xié)程,用 yield 函數(shù)掛起協(xié)程。同樣,從例子中也可以看出如何為執(zhí)行協(xié)程的 resueme 函數(shù)返回多個(gè)值。下面我將逐步解釋上面的代碼。
下面這例子中的協(xié)程使用 yield 函數(shù)和 resume 函數(shù)依次返回?cái)?shù)字 1 到 5。示例中,如果沒有協(xié)程對(duì)象或?qū)ο笠呀Y(jié)束(dead),則重新創(chuàng)建一個(gè)新的協(xié)程對(duì)象;若協(xié)程已經(jīng)存在,則執(zhí)行已經(jīng)存在的協(xié)程。
function getNumber()
local function getNumberHelper()
co = coroutine.create(function ()
coroutine.yield(1)
coroutine.yield(2)
coroutine.yield(3)
coroutine.yield(4)
coroutine.yield(5)
end)
return co
end
if(numberHelper) then
status, number = coroutine.resume(numberHelper);
if coroutine.status(numberHelper) == "dead" then
numberHelper = getNumberHelper()
status, number = coroutine.resume(numberHelper);
end
return number
else
numberHelper = getNumberHelper()
status, number = coroutine.resume(numberHelper);
return number
end
end
for index = 1, 10 do
print(index, getNumber())
end
執(zhí)行上述的程序,我們可以得到如下的輸出結(jié)果:
1 1
2 2
3 3
4 4
5 5
6 1
7 2
8 3
9 4
10 5
大家經(jīng)常會(huì)把協(xié)程和多線程編程語言中的線程進(jìn)行對(duì)比,但我們要明白,協(xié)程有著與線程類似的特性,但是協(xié)程與線程的區(qū)別在于協(xié)程不能并發(fā),任意時(shí)刻只會(huì)有一個(gè)協(xié)程執(zhí)行,而線程允許并發(fā)的存在。(譯注:譯者認(rèn)為本質(zhì)上協(xié)程其是就是線程,不過是用戶態(tài)的線罷了,它將調(diào)度問題交由程序開發(fā)人員手動(dòng)完成。)
我們通過控制程序執(zhí)行順序以滿足獲取某些臨時(shí)信息的需求。配合全局變量的使用,協(xié)和會(huì)變得更加的靈活方便。