0

我正在使用 lua coroutines (lua 5.1) 为应用程序创建插件系统。我希望使用协程,以便插件可以像一个单独的应用程序一样运行,每个处理帧产生一次。插件程序通常遵循如下公式:

function Program(P)
    -- setup --
    NewDrawer(function()
        -- this gets rendered in a window for this plugin program --
        drawstuff(howeveryouwant)
    end)
    -- loop --
    local continue = true
    while continue do
        -- frame by frame stuff excluding rendering (handled by NewDrawer) --
        P = coroutine.yield()
    end
end

每个插件每帧在应用程序的主循环中恢复一次。然后,当绘图开始时,每个插件都有一个单独的窗口,它在其中执行传递给 NewDrawer 的函数。

像这样的东西:

while MainContinue do
    -- other stuff left out --
    ExecutePluginFrames() -- all plugin coroutines resumed once

    BeginRendering()
    -- other stuff left out --
    RenderPluginWindows() -- functions passed to NewDrawer called.
    EndRendering()
end

但是我发现,每当渲染中发生错误时,这突然开始表现得很奇怪,并弄乱了我原本强大的错误处理系统。我花了一点时间来理解正在发生的事情,但似乎我期望在主线程的调用堆栈中的对 WIN:Draw() 的调用(因为它由主应用程序处理)实际上是导致隐式跳转到协程的调用堆栈。

起初的问题是程序突然关闭,没有有用的错误输出。然后在查看插件程序中定义的渲染函数的堆栈回溯后,我发现从主线程到窗口的 Draw 的所有内容都不存在,并且该 yield 在调用堆栈中。

似乎因为窗口是在线程和绘图函数中创建的,它们正在由该线程的调用堆栈处理,这是一个问题,因为这意味着它们不在主线程中设置的 pcall 之外。

这假设会发生吗?它是 C 源代码中的错误/快捷方式的结果吗?我做错了什么或至少做得不够正确?有没有办法干净地处理这个?

4

2 回答 2

0

我发现了为什么在我的情况下会发生这种情况。调用传递给 NewDrawer 的函数的渲染对象在创建时(通过 c 代码)使用指向创建它们的 lua 状态的指针进行初始化,这用于访问它们关联的 lua 数据并用于调用绘图函数。我没有看到 lua_State 和协程之间的联系。因此,事实证明,如果 C 代码导致函数在 yield 之后在堆栈中被调用,则它们可能会被调用。

就解决方案而言,我决定将程序分成两个协程,一个用于渲染,一个用于处理。这解决了这个问题,允许渲染对象的创建线程同时是调用线程,并保持渲染循环和处理循环独立的优点。

于 2013-01-21T21:53:39.893 回答
0

我无法重现您描述的效果。这是我正在运行的代码:

local drawer = {}
function NewDrawer(func)
  table.insert(drawer, func)
end

function Program(P)
    NewDrawer(function()
        print("inside program", P)
    end)
    -- loop --
    local continue = true
    while continue do
        -- frame by frame stuff excluding rendering (handled by NewDrawer) --
        P = coroutine.yield()
    end
end

local coro = coroutine.create(Program)
local MainContinue = true
while MainContinue do
    -- other stuff left out --
    -- ExecutePluginFrames() -- all plugin coroutines resumed once
    coroutine.resume(coro, math.random(10))
    -- RenderPluginWindows() -- functions passed to NewDrawer called.
    for _, plugin in ipairs(drawer) do
      plugin()
    end
    MainContinue = false
end

当我单步执行代码并查看堆栈时,NewDrawer 中设置的回调在“主”线程中被调用。如果你调用coroutine.running()which 返回当前线程或者nil你在主线程中,你可以自己看到它。

于 2013-01-21T01:27:55.800 回答