强制Lua脚本退出
如何结束正在运行的 Lua 脚本?
我有两个线程,一个运行主程序,另一个控制用户提供的 Lua 脚本。我需要终止运行 Lua 的线程,但首先需要使脚本退出。
有没有一种方法可以强制脚本退出?
我已经阅读过建议的方法是返回一个 Lua 异常。然而,不能保证用户的脚本会调用一个 API 函数(它可能在一个紧密的忙循环中)。此外,用户可以使用 pcall
防止错误导致脚本退出。
原文链接 https://stackoverflow.com/questions/6913999
看起来你可以在外部(从你的主线程)终止这个线程,因为 lua 脚本是由用户提供的,你无法向其发送信号来退出。
如果这不是一个选项,你可以尝试使用调试 API。你可以使用 lua_sethook
来启用你重新获得控制的方法,假设你有一种方式在钩子中优雅地终止你的线程。
如果你正在使用协程来启动线程,你可以使用coroutine.yield()
来停止它。
你可以在程序的某个位置设置一个变量,比如叫做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;
}
我还没有找到一种干净的方法来终止正在执行长时间运行的lua脚本的线程,而不依赖于脚本本身的干预。以下是我过去采取的一些方法:
如果脚本是长时间运行的,那么很可能会在某些循环中。脚本可以在每次迭代时检查一些全局变量的值。通过从脚本外部设置此变量,然后可以终止线程。
您可以使用lua_resume启动线程。然后,脚本可以通过使用yield()来退出。
您可以提供自己的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
你可以使用 setjmp
和 longjump
,就像 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;
}
可能并不重要,但在我使用的 Lua 中(如 luaplayer 或 PGELua),我使用以下代码来退出:
os.exit()
或者
pge.exit()
session:destroy();
在您想要销毁 Lua 脚本的地方使用这行代码。
你可能想要看一下 https://github.com/amilamad/preemptive-task-scheduler-for-lua 项目。这是一个针对 Lua 的抢占式任务调度器。它在 hook 内使用了一个 lua_yield 函数,因此你可以暂停你的 Lua 线程。它还内部使用了 longjmp,但它更加安全。
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
结束脚本的方法是通过调用
error
来引发一个错误。然而,如果用户通过pcall
调用脚本,那么这个错误将被捕获。