Lua协程的堆栈进入是隐式的,无需调用resume吗?

我正在使用Lua协程(Lua 5.1)为应用程序创建插件系统。我希望使用协程,这样插件就可以像一个单独的应用程序一样运行,并在每个处理帧后暂停。插件程序通常遵循以下公式:

function Program(P)
--设置--
NewDrawer(function()
--此部分在此插件程序的窗口中呈现--
drawstuff(howeveryouwant)
end)
--循环--
local continue = true
while continue do
--每个帧的事情-除了渲染(由NewDrawer处理)之外--
P = coroutine.yield()
end
end

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

类似于这样:

while MainContinue do
--其他内容省略--
ExecutePluginFrames()--所有插件协程恢复一次

BeginRendering()
--其他内容省略--
RenderPluginWindows()--调用传递给NewDrawer的函数。
EndRendering()
end

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

起初,问题是程序突然关闭,没有有用的错误输出。然后,在查看定义在插件程序中的渲染函数的堆栈回溯时,我发现其余的一切直到主线程的窗口绘制都没有出现在那里,并且yield在调用堆栈中。

似乎因为窗口是在线程中创建的,并且绘图函数也是如此,它们由该线程的调用堆栈处理,这是一个问题,因为这意味着它们在主线程中不在pcall的范围内。

这该怎么办?这是否应该发生?它是C源代码中的错误/捷径的结果?我做错了什么,或者至少没有做正确?是否有一种清洁的处理方式?

点赞
用户1442917
用户1442917

我无法复制您所描述的效果。这是我正在运行的代码:

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

function Program(P)
    NewDrawer(function()
        print("inside program", P)
    end)
    -- 循环 --
    local continue = true
    while continue do
        -- 每一帧的内容,不包括渲染(由NewDrawer处理)--
        P = coroutine.yield()
    end
end

local coro = coroutine.create(Program)
local MainContinue = true
while MainContinue do
    -- 其他的事情略过 --
    -- ExecutePluginFrames() -- 所有插件协程都被恢复一次
    coroutine.resume(coro, math.random(10))
    -- RenderPluginWindows() -- 调用传递给NewDrawer的函数。
    for _, plugin in ipairs(drawer) do
      plugin()
    end
    MainContinue = false
end

当我逐步执行代码并查看堆栈时,NewDrawer中设置的回调被调用在“主”线程中,这应该是正确的。如果您调用coroutine.running(),它将返回当前线程或如果您在主线程内则返回nil

2013-01-21 01:27:55
用户88888888
用户88888888

我已经发现了我这种情况下为什么会发生这种情况。调用传递给NewDrawer的函数的渲染对象是在创建时(由C代码)使用指向创建它们的lua state的指针进行初始化的,这用于访问它们的关联lua数据并调用draw函数。我之前没有看到lua_State和协程之间的关联。所以事实证明,如果C代码导致它们,就有可能在yield后在栈中调用函数。

关于解决方案,我决定将程序分为两个协程,一个用于渲染,一个用于处理。这通过允许创建渲染对象的线程也是调用线程来修复问题,并保持渲染循环和处理循环之间独立性的优点。

2013-01-21 21:53:39