终止 NLua 脚本执行

Terminate NLua script execution

我在我的应用程序中使用 NLua 到 运行 Lua 脚本。我需要实现随时在单独的线程中终止 运行s 脚本的能力,例如用户按下 "Stop" 按钮并且脚本必须立即终止。我读过 SetDebugHook 并尝试关闭 Lua 状态并调用状态错误,但我总是得到 AccessViolationException。

我试过了

Lua env = new Lua(); // created in main thread
env.DoString(); // called in second thread

// Called in main thread
public void Stop()
{
    env.Close(); // Didn't work. AccessViolationException
    env.State.Close(); // Didn't work. AccessViolationException
    env.State.Error("err"); // Didn't work. AccessViolationException
}

尝试用锁同步线程

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

同样的问题。 AccessViolationException

谢谢。

此方法运行良好,使用 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; //force abort after first debughook event

        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; //now the tokens are visible inside the lua
                                 //environment and will reflect changes we make 
                                 //from the main thread
    }

    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 中实现一个函数来检查令牌并在令牌出现时抛出错误是真的:

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

加载到 lua 状态后,我们应该对 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);
    }
}