Lua nested coroutines
我正在尝试在copas中使用redis-lua库。它需要一些修补程序。
一个问题是redis-lua将某些迭代器定义为协程,但是这些迭代器执行可以
的网络操作。
因此,
以下示例显示了问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 |
是否有针对此问题的"标准"解决方案,仍然允许将协程用于迭代器?
我可以通过"标记"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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 |
有更好的解决方案吗?
Lua协程始终屈服于从其恢复的最后一个线程。 Copas套接字函数希望返回到Copas事件循环,但相反,它们陷入了用于实现redis-lua迭代器的协程。不幸的是,除了更改redis-lua迭代器的代码外,您无能为力。之所以没有人这样做的原因是,直到Lua 5.2(LuaJIT也可以做到),才可能无法从迭代器函数产生(迭代器在redis-lua中产生的结果很好,因为它们永远不会离开迭代器)函数,但您不能像Copas套接字函数那样尝试超越
关于使用标记值来区分迭代器收益与其余收益的想法很不错。您只需要确保将所有不用于迭代器函数的yield传递给协程上一级,包括
更具体地说,如果在redis-lua中有这样的代码:
1 2 3 4 5 6 7 8 | -- ... return coroutine.wrap( function() -- ... while true do -- ... coroutine.yield( some_values ) end end ) |
您将其更改为:
1 2 3 4 5 6 7 8 9 10 11 12 | -- ... local co_func = coroutine.wrap( function() -- ... while true do -- ... coroutine.yield( ITERATOR_TAG, some_values ) -- mark all iterator yields end return ITERATOR_TAG -- returns are also intended for the iterator end ) return function() return pass_yields( co_func, co_func() ) -- initial resume of the iterator end |
1 2 3 4 5 6 7 8 9 10 11 12 | local ITERATOR_TAG = {} -- unique value to mark yields/returns local function pass_yields( co_func, ... ) if ... == ITERATOR_TAG then -- yield (or return) intended for iterator? return select( 2, ... ) -- strip the ITERATOR_TAG from results and return else -- pass other yields/resumes back and forth until we hit another iterator -- yield (or return); using tail recursion here instead of a loop makes -- handling vararg lists easier. return pass_yields( co_func, co_func( coroutine.yield( ... ) ) ) end end |
AFAIK,redis-lua开发人员计划在年底之前标记另一个发行版,因此他们可能会对拉取请求表示感谢。
在第一个示例中,您正在使用
但是,您应该
有关详细介绍,请参阅copas文档。