Ограничение времени выполнения встроенного Python

Если я вставляю интерпретатор Python в программу на C или С++, как в в этом примере, есть ли способ ограничить, как долго интерпретатор работает? Есть ли что-нибудь, чтобы остановить код Python от входа в бесконечный цикл и тем самым предотвратить PyObject_CallObject (или эквивалент) от когда-либо возвращаемого?

Аналогично, если код Python создает новый поток, есть ли что-нибудь, чтобы остановить этот поток от входа в бесконечный цикл и работать навсегда?

Ответ 1

Как вы можете видеть в docs, PyObject_CallObject не имеет механизма ограничения времени выполнения функции. Также не существует функции API Python C, о которой я знаю, что позволяет приостановить или убить поток, используемый интерпретатором.

Поэтому мы должны быть немного более креативными в том, как остановить поток. Я могу думать о 3 способах, которыми вы могли бы это сделать (от самого безопасного/чистого до самого опасного...

Опросите ваше основное приложение

Идея здесь в том, что ваша функция Python, которая может работать в течение длительного времени, просто вызывает другую функцию внутри вашего основного приложения, используя API C, чтобы проверить, не следует ли ее отключать. Простой результат True/False позволит вам прекратить изящество.

Это безопасное решение, но требует, чтобы вы изменили свой код на Python.

Использовать исключения

Поскольку вы внедряете Interpreter, вы уже используете API-интерфейс C и поэтому можете использовать PyThreadState_SetAsyncExc, чтобы заставить исключение поднятый в оскорбительной нити. Вы можете найти пример, который использует этот API здесь. Хотя это код Python, одна и та же функция будет работать из вашего основного приложения.

Это решение немного менее безопасно, так как оно требует, чтобы код не улавливал исключение и оставался в работоспособном состоянии впоследствии.

Используйте ОС для завершения потока

Я не собираюсь заниматься этим, потому что он по своей сути небезопасен. Смотрите Есть ли способ убить Thread в Python? для объяснения причин.

Ответ 2

Если вам нужна контрольная лага и джиттер в какой-то системе реального времени, вставьте "StacklessPython" - Python с поддержкой co-подпрограмм. Используется в "Twisted Web Server".

Ответ 3

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

Я использую средний подход из ответа @Peter Brittain от июля 2016 года выше.

После запуска движка Python я получаю идентификатор потока, используя пакет threading в коде Python. Я анализирую это в ac long value и сохраняю его.

Функция прерывания на самом деле довольно проста:
PyGILState_Ensure()
PyThreadState_SetAsyncExc(threadId, PyExc_Exception) с использованием идентификатора потока, который я сохраняю ранее, и универсальный тип исключения
PyGILState_Release()

Так что спасибо как оригинальному вопросу, так и Питеру Бриттену!