0

我正在使用 NLua 在我的应用程序中运行 Lua 脚本。我需要实现随时终止在单独线程中运行的脚本的能力,例如用户按下“停止”按钮并且脚本必须立即终止。我已经阅读了 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

谢谢。

4

1 回答 1

1

这个方法工作得相当好,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);
    }
}
于 2020-06-19T22:32:11.373 回答