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

2024-09-28 05:23:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我有几个用C编写的类,我想从使用Python for.NET的Python编写的应用程序中使用这些类。在

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

为了获得GIL,pythonfor.NET附带了方便的Py.GIL()实用程序,我编写了NoGil以使GIL的发布变得同样容易。在

我假设每当Python代码调用C#代码时,GIL会在调用期间保持不变。但是,似乎GIL不是在方法调用期间保存的,而是在构造函数调用期间保持的,如下面的示例所示。在

下面是C类和myNoGil实用程序类,其中包含一些日志记录。为了简单起见,我没有完整地实现一次性模式。在

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包之后),如下所示:

^{pr2}$

输出是

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>

我用python2.7和3.6(都是64位的)进行了尝试,这是Python for.NET的最新版本2.3.0和.net4.0和4.6.1都是我的目标框架。在

问题:

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

Tags: 代码frommessagenetpublicconsolestategil

热门问题