终止 NLua 脚本执行

我正在使用 NLua 在我的应用程序中运行 Lua 脚本。我需要实现在任何时候终止在单独线程中运行的脚本的能力,例如用户按下“停止”按钮,脚本必须立即终止。我已经阅读了有关 SetDebugHook 的资料,并尝试了关闭 Lua State 并在状态上调用 Error,但我总是会得到 AccessViolationException。

我尝试过

Lua env = new Lua(); // 在主线程中创建
env.DoString(); // 在第二个线程中调用

// 在主线程中调用
public void Stop()
{
    env.Close(); // 不起作用。AccessViolationException
    env.State.Close(); // 不起作用。AccessViolationException
    env.State.Error("err"); // 不起作用。AccessViolationException
}

尝试使用锁同步线程

lock (locker)
{
    if (env.IsExecuting)
        env.Close();
}

相同问题。AccessViolationException

谢谢。

点赞
用户7396148
用户7396148

这种方法,利用 lua_sethook 在执行每行 lua 代码之前检查信号以实现中止,效果还不错:

public partial class NluaThreading : Form
{
    private Lua state;
    private bool abort;
    public NluaThreading()
    {
        InitializeComponent();
    }

    private void Start_Click(object sender, EventArgs e)
    {
        state = new Lua();
        state.SetDebugHook(KeraLua.LuaHookMask.Line, 0);
        state.DebugHook += State_DebugHook;

        abort = true; //强制在第一个 debughook 事件后中止

        new Thread(DoLua).Start();
    }

    private void State_DebugHook(object sender, NLua.Event.DebugHookEventArgs e)
    {
        if (abort)
        {
            Lua l = (Lua)sender;
            l.State.Error("Execution manually aborted");
        }
    }
    private void DoLua()
    {
        try
        {
            state.DoString("while(true) do end");
        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message, "DoLua", MessageBoxButtons.OK);
        }
    }
}

当然,这会增加每行的开销,为了减少这种开销,可以将钩子改为其他值之一。

另一种选择是使用 lua 线程监视并在需要时中止的标记,但这种方法需要在 lua 脚本内进行一些处理:

public partial class NluaThreading : Form
{
    internal class Tokens
    {
        public bool abort = false;
    }

    private Lua state;
    private Tokens tokens;

    public NluaThreading()
    {
       InitializeComponent();

       state = new Lua();
       tokens = new Tokens();
       state["tokens"] = tokens; //现在标记在 lua 内部可见,并将反映我们从主线程进行的更改
    }

    private void Start_Click(object sender, EventArgs e)
    {
        if (!state.IsExecuting)
        {
            tokens.abort = false;
            new Thread(DoLua).Start();
        }
    }

    private void Stop_Click(object sender, EventArgs e) => tokens.abort = true;

    private void DoLua() => state.DoString("repeat print(tokens.abort) until(tokens.abort); print(tokens.abort)");
}

通常情况下,您的 lua 执行会更加复杂,包含许多嵌套的循环,在这些情况下,您可以在 lua 中实现一个函数来检查标记,并在标记为 true 时抛出错误:

function checkTokens()
    if tokens.abort then
        error('Execution manually aborted')
    end
end

然后对 DoLua 函数进行一些更改:

private void DoLua()
{
    try
    {
        state.DoString("while(true) do print(tokens.abort); checkTokens(); end");
    }
    catch(Exception e)
    {
        MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK);
    }
}
2020-06-19 22:32:11