编辑:我还没有找到任何方法来安全地终止 LuaJ 的线程而不修改 LuaJ 本身。以下是我想出的,尽管它不适用于 LuaJ。但是,可以很容易地对其进行修改以在纯 Lua 中完成其工作。我可能会切换到 Java 的 Python 绑定,因为 LuaJ 线程非常有问题。
--- 我想出了以下方法,但它不适用于 LuaJ ---
这是一个可能的解决方案。我使用 debug.sethook 注册了一个钩子,该钩子在“计数”事件上触发(这些事件甚至发生在 a 中while true do end
)。我还传递了一个我创建的自定义“ScriptState”Java 对象,其中包含一个布尔标志,指示脚本是否应该终止。在 Lua 钩子中查询 Java 对象,如果设置了标志,它将抛出错误以关闭脚本 (编辑:抛出错误实际上不会终止脚本)。也可以从 Lua 脚本内部设置终止标志。
如果您希望自动终止不屈不挠的无限循环,实现一个计时器系统非常简单,该系统记录最后一次调用 ScriptState 的时间,然后在没有 API 调用的情况下经过足够的时间自动终止脚本(编辑:这只有效如果线程可以被中断)。如果您想终止无限循环但不中断某些阻塞操作,您可以调整 ScriptState 对象以包含其他允许您暂时暂停自动终止等的状态信息。
这是我的interpreter.lua
,可用于调用另一个脚本并在必要时中断它。它调用 Java 方法,因此它不会在没有 LuaJ(或其他一些 Lua-Java 库)的情况下运行,除非它被修改(编辑:再次,它可以很容易地修改为在纯 Lua 中工作)。
function hook_line(e)
if jthread:getDone() then
-- I saw someone else use error(), but an infinite loop still seems to evade it.
-- os.exit() seems to take care of it well.
os.exit()
end
end
function inithook()
-- the hook will run every 100 million instructions.
-- the time it takes for 100 million instructions to occur
-- is based on computer speed and the calling environment
debug.sethook(hook_line, "", 1e8)
local ret = dofile(jLuaScript)
debug.sethook()
return ret
end
args = { ... }
if jthread == nil then
error("jthread object is nil. Please set it in the Java environment.",2)
elseif jLuaScript == nil then
error("jLuaScript not set. Please set it in the Java environment.",2)
else
local x,y = xpcall(inithook, debug.traceback)
end
这ScriptState
是存储标志的类和main()
要演示的 a:
public class ScriptState {
private AtomicBoolean isDone = new AtomicBoolean(true);
public boolean getDone() { return isDone.get(); }
public void setDone(boolean v) { isDone.set(v); }
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
System.out.println("J: Lua script started.");
ScriptState s = new ScriptState();
Globals g = JsePlatform.debugGlobals();
g.set("jLuaScript", "res/main.lua");
g.set("jthread", CoerceJavaToLua.coerce(s));
try {
g.loadFile("res/_interpreter.lua").call();
} catch (Exception e) {
System.err.println("There was a Lua error!");
e.printStackTrace();
}
}
};
t.start();
try { t.join(); } catch (Exception e) { System.err.println("Error waiting for thread"); }
System.out.println("J: End main");
}
}
res/main.lua
包含要运行的目标 Lua 代码。像往常一样使用环境变量或参数将附加信息传递给脚本。如果您想在 Lua 中使用库,请记住使用JsePlatform.debugGlobals()
而不是。JsePlatform.standardGlobals()
debug
编辑:我刚刚注意到os.exit()
不仅终止了 Lua 脚本,而且还终止了调用进程。它似乎相当于System.exit()
. error()
会抛出错误,但不会导致 Lua 脚本终止。我现在正在尝试为此寻找解决方案。