我試圖在copas內使用redis-lua庫。它需要一些補丁。 一個問題是redis-lua定義了一些迭代器作爲協同程序,但是這些迭代器執行的網絡操作可以是yield
。Lua嵌套協程
因此,coroutine.yield
用於兩個非常不同的事情:對於迭代器和copas。由於網絡調用嵌套在迭代器中,網絡收益被迭代器的coroutine.wrap
攔截,而不是被copas攔截。
下面的例子說明這個問題:
local function iterator()
for i = 1, 2 do
if i == 2 then coroutine.yield() end -- network yield
coroutine.yield() -- iterator yield
end
end
local citerator = coroutine.wrap (iterator)
local function loop() -- use of the iterator within a copas thread
while citerator() do end
end
local cloop = coroutine.create (loop)
while coroutine.resume (cloop) do end -- same as copas loop, executes the cloop thread
是否有一個「標準」解決了這個問題,仍然允許使用協同程序進行迭代器?
我能夠通過「標記」yield
(見下文)做出一個小例子,但它與現有代碼不兼容。我可以不修改代碼copas,但必須更新redis-lua中的迭代器。
local function wrap (f, my_tag)
-- same as coroutine.wrap, but uses my_tag to yield again
local co = coroutine.create (f)
return function()
local t = table.pack (coroutine.resume (co))
local code = t [1]
local tag = t [2]
table.remove (t, 1)
table.remove (t, 1)
if tag == nil then
return
elseif my_tag == tag then
return table.unpack (t)
else
coroutine.yield (tag, table.unpack (t))
end
end
end
local Iterator = {} -- tag for iterator yields
local Network = {} -- tag for network yields
local function iterator()
for i = 1, 2 do
if i == 2 then coroutine.yield (Network, i) end
coroutine.yield (Iterator, i)
end
end
local citerator = wrap (iterator, Iterator)
local function loop()
while citerator() do end
end
local cloop = wrap (loop, Network)
while cloop() do end
有沒有更好的解決方案?
使用你的貢獻,我寫了一個[小模塊](https://github.com/saucisson/nested-coroutines)來(幾乎)透明地替換lua的'coroutine'。它通過[lua測試套件](http://www.lua.org/tests/5.2/)。唯一的變化是要求協程模塊爲:'local coroutine = require「coroutine.make」()'。 – 2014-11-26 16:15:58
@AlbanLinard好主意。我選擇了一種不同的方法來實現這種功能的自動化:我編寫了一個函數,它使用回調函數自動轉換迭代函數(如舊的['table.foreach'](http://www.lua.org/manual/5.0/manual .html#5.4)從Lua 5.0轉換爲for循環迭代器,並使用協程和標記的yield。 – siffiejoe 2014-11-26 17:49:01