一、協同程式基礎 1.什麼是協同程式 協同程式與線程差不多,也就是一條執行序列,擁有自己獨立的棧、局部變數和指令指針(即可以保存變數的值和狀態),同時又與其他協同程式共用全局變數和其他大部分東西。 與線程的區別是具有多個線程的程式可以同時運行幾個線程,而程式任意時刻只能運行一個協同程式,並且協同程式
一、協同程式基礎
1.什麼是協同程式
協同程式與線程差不多,也就是一條執行序列,擁有自己獨立的棧、局部變數和指令指針(即可以保存變數的值和狀態),同時又與其他協同程式共用全局變數和其他大部分東西。
與線程的區別是具有多個線程的程式可以同時運行幾個線程,而程式任意時刻只能運行一個協同程式,並且協同程式只有被顯示地(執行掛起函數)要求掛起才會暫停。
2.四種狀態
(1)掛起(suspended):創建時或執行了操作coroutine.yield()
(2)運行(running):執行了coroutine.resume()
(3)死亡(dead):最後一次調用resume時,協同程式的內容已執行完畢,並且已經返回。resume狀態為dead的協同程式將返回false及錯誤消息,不再執行主函數體。
(4)正常(normal):當一個協同程式A喚醒另一個協同程式B時,A就處於一種特殊狀態——既不是掛機狀態(A不能繼續執行),也不是運行狀態(B在運行)。
3.相關函數(都存放在全局table coroutine中)
(1)create(fun):用於創建協同程式,只有一個參數,就是一個函數(一般為匿名函數),函數中的代碼就是協同程式將要執行的內容。create返回值的類型為thread。創建後的協同程式處於掛起狀態。
(2)resume(co,...):啟動或再次啟動協同程式。參數列表中:co表示將要被啟動的協同程式,後面為變長的可選參數。
(3)yield(...):使正在運行的協同程式掛起,之後可以再恢復運行(調用resume)。只能被協同程式的主函數調用(即在協同程式執行的內容中調用)。參數列表為可選的變長參數。
(4)status(co):檢查協同程式的當前狀態,返回值為四中狀態中的一種。參數co為待檢查的協同程式。
4.有用機制:通過一對resume-yield交換數據
原理:
(1)所有傳遞給resume的額外參數(...)都將視為協同程式主函數的參數。如:
co = coroutine.create ( function(a,b) print("co:",a,b) end ) --創建協同程式
coroutine.resume(co,10,20) --> co:10 20 --啟動協同程式,其中10,20就是額外參數,傳遞給了協同程式的主函數(匿名函數)
(2)resume的返回值中包含傳遞給yield的所有參數。如:
co = coroutine.create( function(a,b)
coroutine.yield(a+b,a-b) --在協同程式的主函數中直接調用yield
i = i+1 --有語法錯誤
end )
第一次調用resume:print(coroutine.resume(co,20,10)) -->true 30 10 --第一個返回值為true表示協同程式執行完畢或執行到yield之前沒有發生錯誤,後面的返回值就是傳遞給yield的參數。
第二次調用resume:print(coroutine.resume(co,20,10)) -->false,attempt to perform arithmetic on global 'i' (a nil value) --resume 第一個返回值為true並不能說明協同程式沒有錯誤。
(3)yield的返回值就是對應resume傳入的額外參數。如:
co = coroutine.create( function() print("co:",coroutine.yield()) end)
coroutine.resume(co) --> --resume中沒有額外參數,則yield不會返回值,協同程式掛起
(4)當協同程式結束時,它的主函數返回的值都將作為resume的返回值。如:
co = coroutine.create( function() return 1,2 end )
print(coroutine.resume(co)) -->true 1 2
二、協同程式的應用
1.生產者-消費者
(1)生產者-消費者一般程式表示:
function producer() --生產者 function consumer() --消費者
while true do while true do
local x = io.read() local x = receive()
send(x) io.write(x,"\n")
end end
end end
問題關鍵:如何將send和receive匹配起來?
(2)生產者-消費者協同程式實現
function send(x) --協同程式主函數中被調用,因而可以調用yield
coroutine.yield(x) --參數x將作為resume的返回值
end
producer = coroutine.create( function() --在這裡可以理解為生產者
while true do
local x = io.read() --輸入,這裡可理解為生產的東西
send(x)
end
end)
function receive() --在這裡可以理解為消費者
local status, value = coroutine.resume(producer) --啟動協同程式
return value
end
程式通過調用消費者receive()來喚醒生產者(即協同程式),然後通過將“生產的”內容x作為參數傳遞給yield,最後作為resume的返回值,賦給value(即消費者得到生產者生產的東西)
實質上利用了一對resume-yield交換數據。