C 中的简单 lua_yield 在 Lua 中没有正确恢复

我刚开始涉足 C 中的 lua 协程与 Lua,我遇到了一个问题,我觉得这应该是我能想到的最简单的例子。

C 代码如下:

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdlib.h>

static int lua_test_yield(lua_State *L) {
        printf("1\n");
        lua_yield(L, 0);
        printf("2\n");
        lua_yield(L, 0);
        printf("3\n");
        lua_yield(L, 0);
        printf("4\n");
        lua_yield(L, 0);
        printf("5\n");
        lua_yield(L, 0);
        return 0;
}

static const struct luaL_Reg mylib[] = {
        {"test_yield", lua_test_yield},

        {NULL, NULL}
        };

// 当从 Lua 中加载 .so 库时使用
int luaopen_mytest(lua_State *L) {
        luaL_newlib(L, mylib);
        return 1;
}

Lua 代码如下:

mytest = require 'mytest'

print("Loaded")
mytest_yeild = coroutine.create(function ()
        mytest.test_yield()
end)

for i=1,5 do
        print(coroutine.resume(mytest_yeild))
end

结果如下:

$ lua test.lua
Loaded
1
true
true
false   cannot resume dead coroutine
false   cannot resume dead coroutine
false   cannot resume dead coroutine

我觉得这非常奇怪。为什么它会报告成功恢复两次,但不打印任何东西然后报告失败的恢复?我在这里错过了什么?谢谢。

点赞
用户734069
用户734069

lua_yield是一个C函数,而C没有一种自动跳回已暂停函数的机制。lua_yield使用C标准库中的longjmp函数随意跳出调用它的函数。但它不能再次返回。

因此,函数中止并退出,返回控制权给调用coroutine.resume的Lua代码。恢复正常,所以打印出true。然后再次恢复协程,它会从调用产生挂起的C函数时的_Lua代码_处开始执行。该代码然后正常退出协程。恢复正常,所以再次打印出true

但协程现在已经耗尽,因此不能恢复。

C函数没有干净的方法可以“恢复”。事实上,Lua 5.1文档明确说明

该函数应仅作为C函数的返回表达式调用,如下所示:

return lua_yield (L, nresults);

Lua5.2及以上版本具有一些恢复C函数的能力,但仅限于通过连续API实现。你不能编写单个C函数,可以像Lua Yielding函数一样工作。

2021-06-13 16:52:52
用户7509065
用户7509065

为了在 yield 后返回到 C 代码,你需要使用 lua_yieldk 并在一个单独的 C 函数中接手,就像下面这样:

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdlib.h>

static int lua_test_yield_6(lua_State *L, int status, lua_KContext ctx) {
        return 0;
}

static int lua_test_yield_5(lua_State *L, int status, lua_KContext ctx) {
        printf("5\n");
        return lua_yieldk(L, 0, 0, lua_test_yield_6);
}

static int lua_test_yield_4(lua_State *L, int status, lua_KContext ctx) {
        printf("4\n");
        return lua_yieldk(L, 0, 0, lua_test_yield_5);
}

static int lua_test_yield_3(lua_State *L, int status, lua_KContext ctx) {
        printf("3\n");
        return lua_yieldk(L, 0, 0, lua_test_yield_4);
}

static int lua_test_yield_2(lua_State *L, int status, lua_KContext ctx) {
        printf("2\n");
        return lua_yieldk(L, 0, 0, lua_test_yield_3);
}

static int lua_test_yield_1(lua_State *L) {
        printf("1\n");
        return lua_yieldk(L, 0, 0, lua_test_yield_2);
}

static const struct luaL_Reg mylib[] = {
        {"test_yield", lua_test_yield_1},

        {NULL, NULL}
};

// 当从 Lua 加载 .so 库时使用
int luaopen_mytest(lua_State *L) {
        luaL_newlib(L, mylib);
        return 1;
}
2021-06-13 17:37:52