Coroutine 教程 |
|
Coroutine 讓我們一次執行多個工作。這是通過有控制地將控制權傳遞給每個常式並等待常式表明它已完成執行來完成的。然後,我們可以重新進入常式以稍後繼續執行,通過重複此操作,我們實現了多工作處理。
Coroutine 在參考手冊的第 2.11 和 5.2 節中進行了說明。[1] [2]
每個工作在執行緒中執行,該執行緒與其他執行緒分開。一次執行多個工作通常稱為多執行緒。因為一次執行多個執行緒,所以我們的應用程式稱為多執行緒的。
有多種方法可以實作多執行緒。有些系統會為每個執行緒分配固定的時間,並在時間到時收回控制權,並將控制權傳給下一個執行緒,依此類推。這種方式稱為優先搶佔式多執行緒。在這種情況下,每個執行緒不需要擔心它佔用的時間,它更注重自己的功能。
在其他系統中,一個執行緒會關注它執行的時間長度。執行緒知道它必須將控制權傳給其他執行緒,以便它們也能夠執行。這種方式稱為協調式或共同作業式多執行緒。在此,所有執行緒都相互協調,以允許應用程式正常運作。這是 Lua 的 coroutine 使用的多工作處理類型。
Lua 中的 Coroutine 不是作業系統執行緒或程序。Coroutine 是在 Lua 中建立的 Lua 程式碼區塊,並且有類似於執行緒的自己的控制流程。一次只執行一個 coroutine,執行直到它啟動另一個 coroutine 或讓出(傳回呼叫它的 coroutine)為止。Coroutine 是一種以便利且自然的方式表達多個共同作業的控制執行緒,但它們不會並行執行,因此從多個 CPU 中不會獲得效能上的好處。然而,由於 coroutine 切換的速度遠快於作業系統執行緒,並且通常不需要複雜且有時昂貴的鎖定機制,因此使用 coroutine 通常比使用完整作業系統執行緒的等效程式更快。
為了讓多個 coroutine 共享執行,它們必須停止執行(在執行合理的處理工作之後)並將控制權傳遞給另一個執行緒。這種提交行為稱為讓步。Coroutine 明確呼叫 Lua 函式 coroutine.yield()
,這類似於在函式中使用 return
。區分讓步與函式傳回的是在稍後我們可以在執行緒中重新進入並從我們離開的位置繼續執行。當您使用 return
退出函式範圍時會銷毀該範圍,我們無法重新進入該範圍,例如:
> function foo(x) >> if x>3 then return true end -- we can exit the function before the end if need be >> return false -- return a value at the end of the function (optional) >> end > = foo(1) false > = foo(100) -- different exit point true
要建立 coroutine,我們必須有一個代表它的函式,例如:
> function foo() >> print("foo", 1) >> coroutine.yield() >> print("foo", 2) >> end >
我們使用 coroutine.create(fn)
函式建立協程。我們傳遞一個 Lua 函式給它,作為執行緒的進入點。Lua 回傳的物件是一個 執行緒
> co = coroutine.create(foo) -- create a coroutine with foo as the entry > = type(co) -- display the type of object "co" thread
我們可以使用 coroutine.status()
函式找出執行緒目前的狀態,例如:
> = coroutine.status(co) suspended
coroutine.resume()
函式開始執行緒。當執行緒讓出時,Lua 會進入執行緒並離開執行緒。> = coroutine.resume(co) foo 1 true
coroutine.resume()
函式會回傳續用的錯誤狀態。輸出顯示我們進入了 foo
函式,並且沒有錯誤地離開。現在是最有趣的部分。如果使用函式,我們無法從離開處繼續,但使用協程,我們可以再次恢復> = coroutine.resume(co) foo 2 true
foo
中讓出之後的行,並且再次沒有錯誤地回傳。但是,如果我們查看狀態,我們可以看到我們離開了 foo
函式,並且協程終止了。> = coroutine.status(co) dead
> = coroutine.resume(co) false cannot resume dead coroutine
以下是一個示範協程一些重要功能的更複雜範例。
> function odd(x) >> print('A: odd', x) >> coroutine.yield(x) >> print('B: odd', x) >> end > > function even(x) >> print('C: even', x) >> if x==2 then return x end >> print('D: even ', x) >> end > > co = coroutine.create( >> function (x) >> for i=1,x do >> if i==3 then coroutine.yield(-1) end >> if i % 2 == 0 then even(i) else odd(i) end >> end >> end) > > count = 1 > while coroutine.status(co) ~= 'dead' do >> print('----', count) ; count = count+1 >> errorfree, value = coroutine.resume(co, 5) >> print('E: errorfree, value, status', errorfree, value, coroutine.status(co)) >> end ---- 1 A: odd 1 E: errorfree, value, status true 1 suspended ---- 2 B: odd 1 C: even 2 E: errorfree, value, status true -1 suspended ---- 3 A: odd 3 E: errorfree, value, status true 3 suspended ---- 4 B: odd 3 C: even 4 D: even 4 A: odd 5 E: errorfree, value, status true 5 suspended ---- 5 B: odd 5 E: errorfree, value, status true nil dead >
基本上,我們有一個 for
迴圈,它在遇到奇數時呼叫 odd()
函式,在遇到偶數時呼叫 even()
函式。輸出可能有點難理解,因此我們將逐一研究以 count
計數的外層迴圈。已新增註解。
---- 1 A: odd 1 -- yield from odd() E: errorfree, value, status true 1 suspended
coroutine.resume(co, 5)
呼叫我們的協程。我們第一次呼叫它時,我們進入協程函式中的 for
迴圈。請注意,我們的協程函式所呼叫的 odd()
函式會讓出。你不必在協程函式中讓出。這是一個重要且有用的功能。我們用讓出回傳 1 的值。
---- 2 B: odd 1 -- resume in odd with the values we left on the yield C: even 2 -- call even and exit prematurely E: errorfree, value, status true -1 suspended -- yield in for loop
for
迴圈會讓出,並暫停協程。這裡要注意的一點是,我們可以在任何地方讓出。我們不必一直從協程中的某一點讓出。我們用讓出回傳 -1。
---- 3 A: odd 3 -- odd() yields again after resuming in for loop E: errorfree, value, status true 3 suspended
for
迴圈中恢復協程,並且在呼叫 odd()
時,它再次讓出。
---- 4 B: odd 3 -- resume in odd(), variable values retained C: even 4 -- even called() D: even 4 -- no return in even() this time A: odd 5 -- odd() called and a yield E: errorfree, value, status true 5 suspended
odd()
的中斷處恢復執行。請注意,變數值會被保留。在協程暫停期間,odd()
函式的範圍會被保留。我們遍历到 even()
的結尾,這次在函式結尾處離開。無論如何,當我們不使用 coroutine.yield()
離開函式時,範圍和其所有變數都會被銷毀。我們只能在讓出時恢復執行。
---- 5 B: odd 5 -- odd called again E: errorfree, value, status true nil dead -- for loop terminates >
odd()
。這次,主要的 for
迴圈達到我們傳遞給 coroutine 的 5 的極限。5 的值和 for
迴圈狀態在 coroutine 的執行過程中都被保留。coroutine 在存在時保留自己的堆疊和狀態。當我們離開 coroutine 函式時,它便會結束,我們無法再使用它。