Lua協同程式


協同程式本質上是共同作業的,它允許兩種或多種方法以受控方式執行。 使用協同程式,在任何給定時間,只有一個協同程式執行,並且此執行協程僅在顯式請求暫停時暫停執行。

上述定義可能看起來含糊不清。 假設有兩種方法,一種是主程式方法,另一種是協程。 當使用resume函式呼叫一個協程時,它會開始執行,當呼叫yield函式時,它會暫停執行。 同樣的協同程式可以繼續執行另一個恢復函式呼叫,協同程式就會暫停。 該過程可以持續到協程執行結束。

協同程式函式

下表列出了Lua中協程的所有可用函式及其相應的用法。

編號 方法 作用或目的
1 coroutine.create (f) 使用函式f建立一個新的協同程式,並返回thread型別的物件。
2 coroutine.resume (co [, val1, ...]) 恢復協程co並傳遞引數(如果有的話)。它返回操作狀態和可選的其他返回值。
3 coroutine.running () 如果在主執行緒中呼叫,則返回正在執行的協同程式或nil
4 coroutine.status (co) 根據協同程式的狀態返回runningnormalsuspendeddead中的一個值。
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,協同程式將兩個變數作為引數。
  • 當呼叫第一個恢復函式時,值32保留在臨時變數value1value2中,直到協程結束。
  • 使用了一個變數tempvar3,它最初值是10,並且通過後續的協程呼叫更新為1316,在整個協程的執行過程中value1的值保持為3
  • 第一個coroutine.yield將兩個值43返回到resume函式,通過更新yield語句中的輸入引數為32。 它還接收協程執行的true/false狀態。
  • 關於協同程式的另一個問題是,在上面的例子中如何處理下一個resume呼叫的句子; 可以看到變數coroutine.yield接收下一個呼叫引數,它提供了一種強大的方法,可以通過保留現有的引數值來進行新的操作。
  • 最後,當協程中的所有語句都執行後,後續呼叫將返回false並且響應語句為:cannot resume dead coroutine

另一個協同程式範例

下面來看一個簡單的協同程式範例,它使用yield函式和resume函式返回15之間的數位。 如果不可用它會建立協程,或者恢復現有的協程。

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

通常會將協同程式與多路程式設計語言的執行緒進行比較,但需要了解協同程式具有類似的執行緒功能,但協同程式一次只執行一個程式,並且永遠不會同時執行。

通過暫時保留某些資訊來控制程式執行順序以滿足需求。 使用帶有協同程式的全域性變數為協同程式提供了更大的靈活性。