【问题标题】:Luabind and coroutinesLuabind 和协程
【发布时间】:2011-09-16 00:01:03
【问题描述】:

我在理解如何通过 luabind 正确使用协程时遇到了一些麻烦。有一个模板函数:

template<class Ret> 
Ret resume_function(object const& obj, ...)

其中 (Ret) 应该包含 Lua 传递给 yield 的值。

我目前的困惑点是:

  • 如果函数返回而不是调用yield,会发生什么? resume_function 是否返回函数的返回值?
  • 如果您不提前知道哪些(或多少)参数将被传递给yield,您应该如何使用此函数?例如,如果有多个可能的屈服函数,该函数可能会调用。
  • 如果将多个值传递给yieldRet 的类型是什么?

我完全误解了这一切是如何运作的吗?我设想这样的事情。在 Lua 方面:

local img = loadImage("foo.png")

loadImage 将是一个 C++ 函数,它请求将图像加载到不同的线程中,然后调用 lua_yield,一段时间后以 img 作为参数调用 luabind::resume_function

我应该将"foo.png" 传递给yield 作为参数吗?在我调用yield 之前到另一个函数,然后从不将任何值传递给yield?构建这个的正确方法是什么?我显然在这里误解了一些东西。

【问题讨论】:

    标签: lua coroutine luabind


    【解决方案1】:

    Where (Ret) 应该包含 Lua 传递给 yield 的值。

    Luabind 只支持单个返回值,所以它只会返回传递给coroutine.yield 的第一个值。

    如果函数返回而不是调用yield会发生什么? resume_function 是否返回函数的返回值?

    是的,你得到了它的返回值。

    如果您不提前知道哪些(或多少)参数将被传递给 yield,您应该如何使用此函数?例如,如果有多个可能的屈服函数,该函数可能会调用。

    这取决于你;它们是你的功能。您必须制定关于产生函数接收什么作为参数以及恢复协程的函数提供什么的约定。

    如果多个值传递给yield,Ret的类型是什么?

    随心所欲。这是模板参数。函数的参数数量与函数提供的返回值无关。

    记住:Lua 函数可以接受任意数量的参数并且可以返回任何值。 Luabind 所能做的就是传递你给它的参数,并将 Lua 函数的返回值转换为你期望的返回值。 Luabind 当然会对返回值进行类型检查。但您有责任确保产生/返回的函数将返回可转换为用户为 Ret 提供的类型的内容。

    loadImage 将是一个 C++ 函数,它请求将图像加载到不同的线程中,然后调用 lua_yield,一段时间后 luabind::resume_function 以 img 作为参数调用。

    如果您使用的是 Luabind,请不要直接致电 lua_yield。在 Luabind 中 yield 的正确方法是向您注册的函数添加一个属性,该属性将在您从函数返回时产生。语法如下:

    module(L)
    [
        def("do_thing_that_takes_time", &do_thing_that_takes_time, yield)
    ];
    

    也就是说,产生的 C++ 函数必须总是产生。这是 Luabind 的一个限制,与常规 Lua 一样,您可以根据需要选择是否屈服。

    另外,不要忘记 Lua 协程与实际线程不同。他们不是先发制人的;他们将在您使用 coroutine.resume 或等效的恢复调用明确告诉他们时执行。

    此外,您应该永远不要从多个 C/C++ 线程运行同一个 Lua 实例; Lua 在同一个实例中不是线程安全的(这或多或少意味着同一个 lua_State 对象)。

    您似乎想要做的是让 Lua 在 C++ 中调用某个函数,该函数本身会产生一个线程来执行某些过程,然后让 Lua 代码等待该线程完成,然后接收它的答案。

    为此,您需要为 Lua 脚本提供一个表示 C++ 线程的对象。所以你的loadImage 函数不应该使用协程逻辑;它应该返回一个代表 C++ 线程的对象。 Lua 脚本可以询问对象是否已完成,如果已完成,则可以从中查询数据。

    协程可以在这里发挥作用的地方是,如果您不希望 Lua 脚本等到它完成。也就是说,你经常调用 Lua 脚本,但如果 C++ 线程没有完成,那么它应该只是返回。在这种情况下,您可以执行以下操作:

    function loadImageAsCoroutine(imageFilename)
        local cppThread = cpp.loadImage(imageFilename);
    
        local function threadFunc(cppThread)
            if(cppThread:isFinished()) then
                local data = cppThread:GetImage();
                return data;
            else
                coroutine.yield();
            end
        end
    
        local thread = coroutine.create(threadFunc);
    
        local errors, data = assert(coroutine.resume(thread, cppThread));
    
        if(coroutine.status(thread) == "dead") then
            return data;
        else
            return thread;
        end
    end
    

    这个函数返回一个协程图像数据本身。这个函数的调用者应该检查类型;如果类型是“线程”,那么 C++ 线程还没有完成。否则为图片数据。

    这个函数的调用者可以用coroutine.resume 的等效值(无论是 luabind::resume_function 还是其他)来抽取他们想要的协程。每次检查返回值。如果 C++ 线程尚未完成,则为 nil,否则为 nil

    【讨论】:

    • 谢谢。您的答案的前半部分非常有帮助。但我认为,后半段并不是解决这个问题的最好方法(很少有轮询)。有了您的意见,我想我脑子里已经有了一幅关于如何编写适当的协程调度程序的图景,该调度程序允许这种阻塞调用按我的意愿工作。
    • 我确实做到了。这很复杂,但我现在可以从 Lua 发出这样的阻塞调用,而无需额外的复杂性或轮询。感谢您的帮助!
    猜你喜欢
    • 2011-09-20
    • 2012-05-20
    • 2012-07-11
    • 2012-03-16
    • 1970-01-01
    • 2011-09-23
    • 2012-05-13
    • 2012-07-20
    • 2019-12-25
    相关资源
    最近更新 更多