Lua嵌套协同路由

Lua嵌套协同路由,lua,nested,coroutine,Lua,Nested,Coroutine,我正试着使用里面的图书馆。它需要一些修补。 一个问题是,将一些迭代器定义为协程,但这些迭代器执行的网络操作可以产生 因此,coroutine.yield用于两种完全不同的用途:用于迭代器和。由于网络调用嵌套在迭代器中,因此网络生成被迭代器的coroutine.wrap截取,而不是被迭代器截取 下面的示例显示了问题: local function iterator () for i = 1, 2 do if i == 2 then coroutine.yield () end -- n

我正试着使用里面的图书馆。它需要一些修补。 一个问题是,将一些迭代器定义为协程,但这些迭代器执行的网络操作可以
产生

因此,
coroutine.yield
用于两种完全不同的用途:用于迭代器和。由于网络调用嵌套在迭代器中,因此网络生成被迭代器的
coroutine.wrap
截取,而不是被迭代器截取

下面的示例显示了问题:

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
s(见下文)来实现一个小示例,但它与现有代码不兼容。我可以不修改代码,但必须在中更新迭代器


有更好的解决方案吗?

在第一个示例中,您使用的是
wrap(loop)
。我想这是copas'
wrap
,因为在这段代码中没有对copas的引用

然而,你应该
copas.wrap()
一个套接字,但是你的
循环
是一个函数


请参阅。

Lua协程总是让位于从中恢复的最后一个线程。Copas套接字函数期望返回到Copas事件循环,但它们却被用于实现redis lua迭代器的协同程序所束缚。不幸的是,除了更改redis lua迭代器的代码之外,您没有什么可以做的。没有人这么做的原因是,直到Lua5.2(LuaJIT也可以这么做)之前,它甚至不可能从迭代器函数中产生(迭代器在redis Lua中产生效果很好,因为它们从未离开迭代器函数,但不能像Copas套接字函数尝试的那样超出
for
循环)

您关于使用标记值来区分迭代器和其他迭代器的想法是好的。您只需确保将所有不适用于迭代器函数的收益传递给上一级的协同例程,包括
coroutine.yield
coroutine.resume
(后者在调用
coroutine.wrap
ed函数时是隐式的)

更具体地说,如果您在redis lua中有这样的代码:

-- ...
return coroutine.wrap( function()
  -- ...
  while true do
    -- ...
    coroutine.yield( some_values )
  end
end )
您可以将其更改为:

-- ...
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
ITERATOR_标记
pass_产生的
函数位于
redis.lua
顶部附近:

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

好了,redis lua开发人员计划在今年年底前为另一个版本添加标签,因此他们可能会感谢pull请求。

我已经编辑了第一个代码列表。
wrap
实际上是一个
coroutine.create
,对cloop的调用现在是
coroutine.resume
。使用您的贡献,我写了一个(几乎)透明地替换lua的
coroutine
的方法。它通过了考试。唯一的更改是要求协同程序模块为:
local coroutine=require“coroutine.make”(
@AlbanLinard Nice idea)。我选择了一种不同的方法来自动化这类事情:我编写了一个函数,该函数使用回调(如Lua5.0中的旧函数)自动将迭代函数转换为
,用于
-循环迭代器,使用协程和标记收益。
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