協同程式本質上是協作的,它允許兩種或多種方法以受控方式執行。 使用協同程式,在任何給定時間,只有一個協同程式運行,並且此運行協程僅在顯式請求暫停時暫停執行。
上述定義可能看起來含糊不清。 假設有兩種方法,一種是主程序方法,另一種是協程。 當使用resume
函數調用一個協程時,它會開始執行,當調用yield
函數時,它會暫停執行。 同樣的協同程式可以繼續執行另一個恢復函數調用,協同程式就會暫停。 該過程可以持續到協程執行結束。
協同程式函數
下表列出了Lua中協程的所有可用函數及其相應的用法。
編號 | 方法 | 作用或目的 |
---|---|---|
1 | coroutine.create (f) |
使用函數f 創建一個新的協同程式,並返回thread 類型的對象。 |
2 | coroutine.resume (co [, val1, ...]) |
恢復協程co 並傳遞參數(如果有的話)。它返回操作狀態和可選的其他返回值。 |
3 | coroutine.running () |
如果在主線程中調用,則返回正在運行的協同程式或nil 。 |
4 | coroutine.status (co) |
根據協同程式的狀態返回running ,normal ,suspended 或dead 中的一個值。 |
5 | coroutine.wrap (f) |
與coroutine.create 一樣,coroutine.wrap 函數也會創建一個協同程式,但它不會返回協同程式本身,而是返回一個函數,當調用它時,它會恢復協同程式。 |
6 | coroutine.yield (...) |
暫停正在運行的協同程式。 傳遞給此方法的參數充當resume 函數的附加返回值。 |
示例
下麵來看一個例子,通過此示例來理解協同程式的概念。
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))
當運行上面的程式時,將得到以下輸出 -
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
函數來啟動操作和yield
函數來停止操作。 此外,可以看到coroutine
的恢復功能接收到多個返回值。
- 首先,創建一個協同程式並分配給變數名稱
co
,協同程式將兩個變數作為參數。 - 當調用第一個恢復函數時,值
3
和2
保留在臨時變數value1
和value2
中,直到協程結束。 - 使用了一個變數
tempvar3
,它最初值是10
,並且通過後續的協程調用更新為13
和16
,在整個協程的執行過程中value1
的值保持為3
。 - 第一個
coroutine.yield
將兩個值4
和3
返回到resume
函數,通過更新yield
語句中的輸入參數為3
和2
。 它還接收協程執行的true/false
狀態。 - 關於協同程式的另一個問題是,在上面的例子中如何處理下一個
resume
調用的句子; 可以看到變數coroutine.yield
接收下一個調用參數,它提供了一種強大的方法,可以通過保留現有的參數值來進行新的操作。 - 最後,當協程中的所有語句都執行後,後續調用將返回
false
並且回應語句為:cannot resume dead coroutine。
另一個協同程式示例
下麵來看一個簡單的協同程式示例,它使用yield
函數和resume
函數返回1
到5
之間的數字。 如果不可用它會創建協程,或者恢復現有的協程。
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
當運行上面的程式時,將得到以下輸出。
1 1
2 2
3 3
4 4
5 5
6 1
7 2
8 3
9 4
10 5
通常會將協同程式與多路程序設計語言的線程進行比較,但需要瞭解協同程式具有類似的線程功能,但協同程式一次只執行一個程式,並且永遠不會同時執行。
通過暫時保留某些資訊來控制程式執行順序以滿足需求。 使用帶有協同程式的全局變數為協同程式提供了更大的靈活性。