我使用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())
中包装代码的目的是什么。因为有或没有它我的脚本运行方式相同。
答案 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似乎不支持等效。