C# 为什么Python for.NET在构造函数调用期间持有全局解释器锁,而不是方法调用期间持有全局解释器锁?

C# 为什么Python for.NET在构造函数调用期间持有全局解释器锁,而不是方法调用期间持有全局解释器锁?,c#,python,multithreading,python.net,pythonnet,C#,Python,Multithreading,Python.net,Pythonnet,我有几个用C#编写的类,我想从使用Python for.NET用Python编写的应用程序中使用这些类 之前使用Python4Delphi和Python的C-API做过类似的事情,我知道为与Python交互的所有东西保持全局解释器锁(GIL)是多么重要,同时在长时间运行的操作中释放它,以便其他Python线程可以运行 为了获得GIL,Python For.NET附带了方便的Py.GIL()实用程序,我编写了NoGil,使GIL的发布变得同样容易 我假设每当Python代码调用C#代码时,GIL都

我有几个用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
实用程序类,其中包含一些日志记录。为了简单起见,我没有完全实现一次性模式

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>
在从构造函数调用BeginAllowThreads之前
从构造函数调用BeginAllowThreads后
执行构造函数
在从构造函数调用EndAllowThreads之前
从构造函数调用EndAllowThreads后
在从方法调用BeginAllowThreads之前
致命的Python错误:PyEval_SaveThread:NULL tstate
当前线程0x0000124c(最新调用优先):
文件“test.py”,第5行
我用Python2.7和3.6(均为64位)、Python for.NET的最新版本2.3.0以及.NET4.0和4.6.1作为我的目标框架来尝试这一点

问题:

  • 这是Python for.NET中的预期行为还是我应该提交一个bug
  • 如果是预期的,那么在调用.NET代码时,我可以假设GIL被保留的具体情况是什么?我没有找到任何关于这方面的文件
  • 或者,我是否应该从不假设GIL的任何内容,并且总是在必要时获取它(即,当通过委托调用Python代码时)?那么,如何确保在长时间的非Python操作中不保留它呢

  • 您是否可以重新发布输出,以代码格式而不是引号格式,这样我们就可以确切地确定代码生成了什么?出于某种原因。重新格式化了输出。谢谢你的链接。看起来,除非一个方法标记有
    [禁止pythonthreads]
    属性(不幸的是它是内部的),否则GIL将在调用之前释放。然而,对于构造函数来说并非如此。允许线程和获取GIL有点不同,例如,请参见本PR讨论: