Вопрос

Я встраиваю интерпретатор Python в программу на языке Си.Однако может случиться так, что при запуске какого-либо скрипта python через PyRun_SimpleString() запустится в бесконечный цикл или будет выполняться слишком долго.Рассмотреть PyRun_SimpleString("while 1: pass"); Предотвращая блокировку основной программы, я подумал, что смогу запустить интерпретатор в потоке.

Как мне остановить выполнение скрипта python во встроенном интерпретаторе, запущенном в потоке, не останавливая весь процесс?

Можно ли передать исключение интерпретатору?Должен ли я обернуть скрипт под какой-нибудь другой скрипт, который бы прослушивал сигналы?

PS:Я мог бы запустить python в отдельном процессе, но это не то, чего я хочу - если только это не последнее средство...


Обновить:

Итак, теперь это работает.Еще раз спасибо вам, Денис Откидач!

Если я правильно понимаю, вам нужно сделать две вещи:скажите переводчику, чтобы он остановился и return -1 в том же потоке, что и ваша функция PyRun_SimpleString().

Чтобы остановиться, у человека есть несколько возможностей: PyErr_SetString(PyExc_KeyboardInterrupt, "...") или PyErr_SetInterrupt() - первый может заставить Python выполнить еще несколько инструкций, а затем он останавливается, более поздний немедленно останавливает выполнение.

Для return -1 вы используете Py_AddPendingCall() чтобы внедрить вызов функции в выполнение Python.Документы упоминают это начиная с версий 2.7 и 3.1, но оно также работает на более ранних Pythons (2.6 здесь).Начиная с 2.7 и 3.1, он также должен быть потокобезопасным, что означает, что вы можете вызывать его, не приобретая GIL (?).

Таким образом, можно было бы переписать приведенный ниже пример:

int quit() {
    PyErr_SetInterrupt();
    return -1;
}
Это было полезно?

Решение

Вы можете использовать Py_AddPendingCall() чтобы добавить функцию, вызывающую исключение, которое будет вызываться при следующем интервале проверки (см. Документы по sys.setcheckinterval() для получения дополнительной информации).Вот пример с Py_Exit() вызовите (который работает у меня, но, вероятно, это не то, что вам нужно), замените его на Py_Finalize() или один из PyErr_Set*():

int quit(void *) {
    Py_Exit(0);
}


PyGILState_STATE state = PyGILState_Ensure();
Py_AddPendingCall(&quit, NULL);
PyGILState_Release(state);

Этого должно быть достаточно для любого кода на чистом python.Но обратите внимание, что некоторые функции C могут некоторое время выполняться как одна операция (был пример с длительным поиском по регулярному выражению, но я не уверен, что это все еще актуально).

Другие советы

Что ж, интерпретатор python должен быть запущен в отдельном потоке от программы встраивания, иначе у вас просто никогда не будет возможности прервать работу интерпретатора.

Когда у вас это будет, возможно, вы сможете использовать один из вызовов исключений Python API для запуска исключения в интерпретаторе?Видишь Исключения из API Python C

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

Рассматривая этот вопрос в 2019 году, решение здесь у меня не сработало;Я пытался использовать Py_AddPendingCall(&quit, NULL); довольно несколькими различными способами, но вызванная функция никогда не выполнялась должным образом или вообще не выполнялась.Это привело к зависанию движка Python и, в конечном итоге, к сбою при отправке Py_FinalizeEx().Наконец-то я нашел то, что мне было нужно, в этом очень полезном ответ на аналогичный вопрос.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top