Завершить выполнение сценария N Lua - PullRequest
0 голосов
/ 19 июня 2020

Я использую N Lua для запуска сценария Lua в моем приложении. Мне нужно реализовать возможность завершения сценария, который выполняется в отдельном потоке в любое время, например, пользователь нажимает кнопку «Стоп», и сценарий должен немедленно завершаться. Я читал о SetDebugHook и пытался закрыть Lua State и вызвать ошибку в состоянии, но всегда получаю 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

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

Та же проблема. AccessViolationException

Спасибо.

1 Ответ

1 голос
/ 20 июня 2020

Этот метод работает достаточно хорошо, используя 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);
    }
}
...