Pythonnet如何正确停止脚本执行

时间:2018-04-19 13:21:40

标签: c# python .net multithreading python.net

我使用Pythonnet将Python脚本启动器嵌入到C#WPF应用程序中。我可以使用Scope将变量传递给python脚本,然后使用MVVM模式在控制台上获得结果。

现在我想允许用户随时停止脚本执行。我无法找到如何正常工作以便正确关闭线程。

class PythonRuntime
{
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    private MainViewModel viewModel;
    private string pythonCode;

    private bool runtimeThreadLock = false;
    Thread thread;

    private PyScope scope;
    private dynamic pyThread;
    private dynamic pyLock;

    ConsoleWriter consoleWriter;


    public PythonRuntime(MainViewModel viewModel, ConsoleWriter consoleWriter)
    {
        this.viewModel = viewModel;
        this.consoleWriter = consoleWriter;
        SetUpPython();    
    }

    public string PythonCode { get => pythonCode; set => pythonCode = value; }

    private void SetUpPython()
    {
        PythonEngine.Initialize(true);

        scope = Py.CreateScope();
        // consoleWriter to make python prints into C# UI
        scope.Set("Console", consoleWriter);
    }

    public void LaunchScript()
    {
        if (!runtimeThreadLock)
        {
            thread = new Thread(PythonNetTest);                
            thread.Start();
        }
    }

    public void StopScript()
    {
        // ???
    }

    [HandleProcessCorruptedStateExceptions]
    private void PythonNetTest()
    {
        runtimeThreadLock = true;
        pyThread = PythonEngine.BeginAllowThreads();
        pyLock = PythonEngine.AcquireLock();

        using (Py.GIL())
        {
            try
            {
                scope.Exec(pythonCode);
            }
            catch (PythonException exception)
            {
                consoleWriter.WriteError(exception.ToString());
            }
        }

        PythonEngine.ReleaseLock(pyLock);
        PythonEngine.EndAllowThreads(pyThread);
        runtimeThreadLock = false;
    }
}  

除了我的问题,我想知道在using(Py.GIL())中包装代码的目的是什么。因为有或没有它我的脚本运行方式相同。

  • Pythonnet:2.4.0
  • Python:2.7.2 32位
  • NetFramework:4.7.1

1 个答案:

答案 0 :(得分:3)

好的,我刚刚开始嵌入CPython的工作,可能只知道比你多一点。那个警告......

首先,您需要让脚本终止。当它执行时,将返回对.Exec()的调用,并且该线程将退出。如果你的脚本运行了一段有限的时间,那么你就等待它。否则,你必须安排一些它应该退出的信号。

其次,主线将使用以下所述的几种.NET模式之一等待线程完成:How to wait for thread to finish with .NET?

using(Py.GIL())PythonEngine.AcquireLock();PythonEngine.ReleaseLock(pyLock);的简写。它会创建一个IDisposable对象来获取锁,然后在Dispose()上释放它。因此,在您的示例中,它是多余的。

我不确定您对BeginAllowThreads()的通话效果。文档说它释放锁以允许其他线程。当你打电话给你时,你没有GIL。下一行获取GIL。因此,它似乎对我没有任何作用。

有关线程的详细信息,请参阅https://docs.python.org/3/c-api/init.html。这似乎与python线程和保存线程状态更相关,因此可以完成其他非python事务。这是python 3. Python 2似乎不支持等效。