Python.Net: нужно ли использовать (Py.GIL()) в обратном вызове из .Net в Python?

На странице Python.Net https://github.com/pythonnet/pythonnet говорится:

Все вызовы python должны быть внутри блока using (Py.GIL()) {/* Здесь находится ваш код */}.

У меня есть приложение Python, которое вызывает .Net dll. .Net dll запускает новый поток, который выполняет некоторую работу, а затем вызывает обратно в Python. Если я оборачиваю обратный вызов в использование (Py.GIL()), то мой код печатает следующую ошибку, когда основной поток Python завершает работу:

Fatal Python error: auto-releasing thread-state, but no thread-state for this thread
Python runtime state: finalizing (tstate=00890970)

В моем коде обратный вызов все еще обрабатывается функцией python_callback после завершения основного потока Python. Я хочу, чтобы все останавливалось чисто, когда завершается основной поток Python.

Если я НЕ оборачиваю обратный вызов в использование (Py.GIL()), то он работает нормально. Итак, мой вопрос:

Нужно ли оборачивать обратный вызов из .Net в Python с помощью (Py.GIL())?

Если да: как мне избежать этого сообщения об ошибке?

Код Python:

import time
from datetime import datetime
import clr
clr.AddReference("System")
clr.AddReference(r"C:\MyDotNet\MyDotNet.dll")
import System
from MyDotNet import MyDotNetClass

def main():
    print(datetime.now(), 'Python started')
    dot_net_obj = MyDotNetClass(System.Action[System.Object](python_callback))
    time.sleep(10)
    print(datetime.now(), 'Python main thread finished')

def python_callback(arg):
    print(datetime.now(), 'Python: In callback, arg =', arg)
    time.sleep(20)    # <-- to keep the callback running when the main thread finishes

if __name__ == '__main__':
    main()

.Net-код:

using System;
using System.Threading;
using Python.Runtime;

namespace MyDotNet
{
    public class MyDotNetClass
    {
        public MyDotNetClass(Delegate callback)
        {
            new Thread(() =>
            {
                Thread.Sleep(3000);

                using (Py.GIL())    // <--- Is it safe to remove this 'using (Py.GIL())'?
                {
                    object[] paramToPass = new object[1];
                    paramToPass[0] = "The C# work is done";
                    callback.DynamicInvoke(paramToPass);
                }
            }).Start();
        }
    }
}

И вот результат:

2020-11-23 10:10:30.168585 Python started
2020-11-23 10:10:33.191054 Python: In callback, arg = The C# work is done
2020-11-23 10:10:40.191273 Python main thread finished
Fatal Python error: auto-releasing thread-state, but no thread-state for this thread
Python runtime state: finalizing (tstate=00890970)

Использование Python 3.8, Python.Net 2.5.1, .Net Framework 4.6.1


person Vince    schedule 23.11.2020    source источник


Ответы (1)


Да, если речь идет о резьбе. Просмотрите проблему в репозитории Python.NET

person LOST    schedule 25.11.2020
comment
Спасибо. Проблема в том, что если я сохраняю оболочку using (Py.GIL()), она печатает ошибку Fatal Python error: автоматическое освобождение состояния потока, но нет состояния потока для этого потока, когда основной поток Python завершается. Я делаю что-то неправильно? - person Vince; 26.11.2020
comment
Вы должны убедиться, что все другие потоки, которые когда-либо запускали код Python, остановлены, прежде чем пытаться закрыть основной поток. - person LOST; 26.11.2020
comment
Мое приложение Python предоставляет API для пользовательских сценариев Python, поэтому у меня нет предупреждений, когда основной поток завершается. Обратные вызовы из .Net в Python могут происходить в любое время. Когда поток передается из .Net в Python, я вижу, что это поток демона. Насколько я понимаю, когда основной поток Python завершается, он автоматически завершает все потоки демона. Это то, что я хочу - я не возражаю, чтобы они были немедленно прекращены. Почему Python.Net не может молча разрешить другим потокам останавливаться после завершения основного потока? - person Vince; 01.12.2020
comment
Я нашел обходной путь: в python_callback, если я запускаю новый поток демона для выполнения работы, ошибка не печатается. Поведение Python.Net по-прежнему кажется неправильным, потому что оно непоследовательно в зависимости от того, какой поток демона занят, когда основной поток завершается: - Создано в .Net, затем передано в Python: Получите «Фатальная ошибка Python» - Создано в .Net, передано в Python, который запускает новый поток Python: нет ошибки - person Vince; 01.12.2020