强制Lua脚本退出

如何结束正在运行的 Lua 脚本?

我有两个线程,一个运行主程序,另一个控制用户提供的 Lua 脚本。我需要终止运行 Lua 的线程,但首先需要使脚本退出。

有没有一种方法可以强制脚本退出?

我已经阅读过建议的方法是返回一个 Lua 异常。然而,不能保证用户的脚本会调用一个 API 函数(它可能在一个紧密的忙循环中)。此外,用户可以使用 pcall 防止错误导致脚本退出。

原文链接 https://stackoverflow.com/questions/6913999

点赞
stackoverflow用户107090
stackoverflow用户107090

结束脚本的方法是通过调用 error 来引发一个错误。然而,如果用户通过 pcall 调用脚本,那么这个错误将被捕获。

2011-08-02 16:41:17
stackoverflow用户151501
stackoverflow用户151501

看起来你可以在外部(从你的主线程)终止这个线程,因为 lua 脚本是由用户提供的,你无法向其发送信号来退出。

如果这不是一个选项,你可以尝试使用调试 API。你可以使用 lua_sethook 来启用你重新获得控制的方法,假设你有一种方式在钩子中优雅地终止你的线程。

2011-08-02 17:38:10
stackoverflow用户677756
stackoverflow用户677756

如果你正在使用协程来启动线程,你可以使用coroutine.yield()来停止它。

2011-08-02 18:19:51
stackoverflow用户63791
stackoverflow用户63791

你可以在程序的某个位置设置一个变量,比如叫做forceQuitLuaScript。然后,你可以使用这里描述的钩子来运行每n个指令。在n个指令之后,它会运行你的钩子,该钩子只是检查forceQuitLuaScript是否被设置,如果设置了,就做需要做的任何清理工作,并杀死线程。

编辑:这里有一个廉价的示例,展示了它如何工作,只是单线程。这只是为了说明如何处理pcall等:

#include <stdlib.h>
#include "lauxlib.h"

void hook(lua_State* L, lua_Debug *ar)
{
    static int countdown = 10;
    if (countdown > 0)
    {
        --countdown;
        printf("countdown: %d!\n", countdown);
    }
    else
    {
        // 从现在开始,只要执行了一行,就会产生错误
        // 错误直到你的脚本到达顶部才会停止
        lua_sethook(L, hook, LUA_MASKLINE, 0);
        luaL_error(L, "");
    }
}

int main(int argc, const char *argv[])
{
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);
    lua_sethook(L, hook, LUA_MASKCOUNT, 100);
    // 无限递归到pcalls中
    luaL_dostring(L, "function test() pcall(test) print 'recursing' end pcall(test)");
    lua_close(L);
    printf("Done!");
    return 0;
}
2011-08-15 19:48:27
stackoverflow用户83497
stackoverflow用户83497

我还没有找到一种干净的方法来终止正在执行长时间运行的lua脚本的线程,而不依赖于脚本本身的干预。以下是我过去采取的一些方法:

  1. 如果脚本是长时间运行的,那么很可能会在某些循环中。脚本可以在每次迭代时检查一些全局变量的值。通过从脚本外部设置此变量,然后可以终止线程。

  2. 您可以使用lua_resume启动线程。然后,脚本可以通过使用yield()来退出。

  3. 您可以提供自己的pcall实现,以检查特定类型的错误。然后,脚本可以使用自定义错误类型调用error(),您的pcall版本可以监视该错误类型:

    function()
         local there_is_an_error = do_something()
         if (there_is_an_error) then
             error({code = 900, msg = "Custom error"})
         end
    end
    
2011-08-16 16:33:59
stackoverflow用户63791
stackoverflow用户63791

你可以使用 setjmplongjump,就像 Lua 库在内部所做的那样。这将使你能够安全地退出 pcalls ,而无需一直出错,从而防止脚本试图处理您的伪错误,并仍然让您退出执行。(尽管我不知道这在线程中的效果如何。)

#include <stdio.h>
#include <setjmp.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

jmp_buf place;

void hook(lua_State* L, lua_Debug *ar)
{
    static int countdown = 10;
    if (countdown > 0)
    {
        --countdown;
        printf("countdown: %d!\n", countdown);
    }
    else
    {
        longjmp(place, 1);
    }
}

int main(int argc, const char *argv[])
{
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);
    lua_sethook(L, hook, LUA_MASKCOUNT, 100);

    if (setjmp(place) == 0)
        luaL_dostring(L, "function test() pcall(test) print 'recursing' end pcall(test)");

    lua_close(L);
    printf("Done!");
    return 0;
}
2011-08-16 19:10:08
stackoverflow用户1139673
stackoverflow用户1139673

可能并不重要,但在我使用的 Lua 中(如 luaplayer 或 PGELua),我使用以下代码来退出:

os.exit()

或者

pge.exit()
2012-01-11 16:15:44
stackoverflow用户1325708
stackoverflow用户1325708
session:destroy();

在您想要销毁 Lua 脚本的地方使用这行代码。

2014-05-21 12:56:11
stackoverflow用户901993
stackoverflow用户901993

你可能想要看一下 https://github.com/amilamad/preemptive-task-scheduler-for-lua 项目。这是一个针对 Lua 的抢占式任务调度器。它在 hook 内使用了一个 lua_yield 函数,因此你可以暂停你的 Lua 线程。它还内部使用了 longjmp,但它更加安全。

2018-01-17 01:18:59