Почему sys.exit() не завершается при вызове внутри потока в Python?
Это может быть глупым вопросом, но я проверяю некоторые из моих предположений о Python, и меня смущает вопрос, почему следующий фрагмент кода не будет выходить при вызове в потоке, а завершится при вызове в основном потоке.
import sys, time
from threading import Thread
def testexit():
time.sleep(5)
sys.exit()
print "post thread exit"
t = Thread(target = testexit)
t.start()
t.join()
print "pre main exit, post thread exit"
sys.exit()
print "post main exit"
Документы для sys.exit() утверждают, что вызов должен выйти из Python. Из результатов этой программы я вижу, что "выход из пост-потока" никогда не печатается, но основной поток просто продолжает работать даже после того, как поток вызывает выход.
Создается ли отдельный экземпляр интерпретатора для каждого потока, и вызов exit () просто выходит из этого отдельного экземпляра? Если да, то как реализация многопоточности управляет доступом к общим ресурсам? Что если я захочу выйти из программы (не то, что я на самом деле хочу, а просто, чтобы я понял)?
6 ответов
sys.exit() вызывает исключение SystemExit, как и thread.exit(). Таким образом, когда sys.exit() вызывает это исключение внутри этого потока, это имеет тот же эффект, что и вызов thread.exit(), именно поэтому выходит только поток.
Что если я захочу выйти из программы (не то, что я на самом деле хочу, а просто, чтобы я понял)?
По крайней мере, в Linux вы можете сделать что-то вроде:
os.kill(os.getpid(), signal.SIGINT)
Это отправляет SIGINT в основной поток, где он может быть обработан как KeyboardInterrupt.
Кстати: в Windows вы можете отправлять только сигнал SIGTERM, который не может быть пойман с Python. (там я бы предпочел позвонить os._exit, как предложил Хельмут)
Что если я захочу выйти из программы?
Помимо описанного Дестаном метода вы можете позвонить os._exit
(обратите внимание на подчеркивание). Перед использованием убедитесь, что вы понимаете, что он не выполняет очистки (например, вызов __del__
или похожие).
Что если я захочу выйти из программы (не то, что я на самом деле хочу, а просто, чтобы я понял)?
Мой предпочтительный метод - передача сообщений Erlang-ish. Слегка упрощенно, я делаю это так:
import sys, time
import threading
import Queue # thread-safe
class CleanExit:
pass
ipq = Queue.Queue()
def testexit(ipq):
time.sleep(5)
ipq.put(CleanExit)
return
threading.Thread(target=testexit, args=(ipq,)).start()
while True:
print "Working..."
time.sleep(1)
try:
if ipq.get_nowait() == CleanExit:
sys.exit()
except Queue.Empty:
pass
Тебя беспокоит тот факт, что "до главного выхода, после выхода из поста" напечатано?
В отличие от некоторых других языков (например, Java), где аналог sys.exit
(System.exit
в случае Java) вызывает немедленную остановку виртуальной машины / процесса / интерпретатора, sys.exit
просто вызывает исключение, в частности исключение SystemExit.
Вот документы для sys.exit
(просто print sys.exit.__doc__
):
Выйдите из интерпретатора, подняв SystemExit (статус).
Если статус опущен или отсутствует, по умолчанию используется ноль (т. Е. Успех).
Если статус числовой, он будет использоваться в качестве статуса выхода из системы.
Если это другой вид объекта, он будет напечатан и система
статус выхода будет один (т. е. сбой).
Это имеет несколько последствий:
- в потоке он просто убивает текущий поток, а не весь процесс (при условии, что он достигает самого верха стека...)
- деструкторы объекта (
__del__
) могут быть вызваны, поскольку стековые фреймы, ссылающиеся на эти объекты, размотаны - наконец, блоки выполняются, когда стек раскручивается
- Вы можете поймать
SystemExit
исключение
Последнее, пожалуй, самое удивительное и еще одна причина, по которой у вас почти никогда не должно быть неквалифицированного except
утверждение в вашем коде Python.
_thread.interrupt_main()
доступен начиная с Python 3.7 (до этого необязательно)