2

我有几个用 C# 编写的类,我想从使用 Python for .NET 的 Python 编写的应用程序中使用它们。

之前使用 Python4Delphi 和 Python 的 C API 做过类似的事情,我知道为与 Python 交互的所有内容持有全局解释器锁 (GIL) 是多么重要,同时在长时间运行的操作期间释放它以便其他 Python 线程可以运行。

为了获得 GIL,Python for .NET 附带了方便的Py.GIL()实用程序,我编写NoGil它是为了让发布 GIL 变得同样容易。

我曾假设每当 Python 代码调用 C# 代码时,GIL 都会在调用期间保留。不过,似乎 GIL 在方法调用期间并未保留,而在构造函数调用期间则保留,如下面的示例所示。

这是 C# 类和我的NoGil带有一些日志记录的实用程序类。为简单起见,我没有完全实现 Disposable 模式。

using Python.Runtime;
using System;

namespace PythonNet
{
    public class Class1
    {
        public Class1()
        {
            using (new NoGil("constructor"))
            {
                Console.WriteLine("executing constructor");
            }
        }

        public void Method()
        {
            using (new NoGil("method"))
            {
                Console.WriteLine("executing method");
            }
        }
    }

    public class NoGil: IDisposable
    {
        private string _message;
        private IntPtr _state = IntPtr.Zero;

        public NoGil(string message)
        {
            _message = message;
            Console.WriteLine("Before calling BeginAllowThreads from " + message);
            _state = PythonEngine.BeginAllowThreads();
            Console.WriteLine("After calling BeginAllowThreads from " + _message);
        }

        public void Dispose()
        {
            if (_state == IntPtr.Zero)
            {
                Console.WriteLine("B_state == IntPtr.Zero in " + _message);
            }
            else
            {
                Console.WriteLine("Before calling EndAllowThreads from " + _message);
                PythonEngine.EndAllowThreads(_state);
                Console.WriteLine("After calling EndAllowThreads from " + _message);
            }
        }
    }
}

它在 Python 中使用(在通过 pip 安装了 pythonnet 包之后),如下所示:

import clr
clr.AddReference("PythonNet.dll")
from PythonNet import Class1
c = Class1()
c.Method()

输出是

Before calling BeginAllowThreads from constructor
After calling BeginAllowThreads from constructor
executing constructor
Before calling EndAllowThreads from constructor
After calling EndAllowThreads from constructor
Before calling BeginAllowThreads from method
Fatal Python error: PyEval_SaveThread: NULL tstate

Current thread 0x0000124c (most recent call first):
  File "test.py", line 5 in <module>

我尝试使用 Python 2.7 和 3.6(均为 64 位)、适用于 .NET 的 Python 的最新版本 2.3.0 以及 .NET 4.0 和 4.6.1 作为我的目标框架。

问题:

  1. 这是 Python for .NET 中的预期行为还是我应该提交错误?
  2. 如果可以预期,我可以假设在调用 .NET 代码时持有 GIL 的情况到底是什么?我没有找到任何关于此的文档。
  3. 或者我不应该对 GIL 做任何假设,并且总是在必要时获取它(即通过委托调用 Python 代码时)?那么我如何确保在长时间的非 Python 操作期间不会持有它?
4

0 回答 0