Почему 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. Я вижу из вывода этой программы, что" post thread exit " никогда не печатается, но основной поток просто продолжает идти даже после того, как поток вызывает exit.

является ли отдельный экземпляр интерпретатора создается для каждого потока, и вызов exit() просто выходит из этого отдельного экземпляра? Если да, то как потоковая реализация управляет доступом к общим ресурсам? Что делать, если я действительно хочу выйти из программы из потока (не то, что я действительно хочу, но просто так понимаю)?

5 67

5 ответов:

sys.exit () вызывает исключение SystemExit, как и thread.выход.)( Так, когда сис.exit () вызывает это исключение внутри этого потока, оно имеет тот же эффект, что и вызов потока.exit (), поэтому выходит только поток.

Что делать, если я хочу выйти из программы из потока (не то что я на самом деле хочу, но только так я понимаю)?

по крайней мере на Linux, вы могли бы сделать что-то вроде:

os.kill(os.getpid(), signal.SIGINT)

это отправить SIGINT в основной поток, где он может быть обработан как KeyboardInterrupt.

BTW: в Windows вы можете отправлять только сигнал SIGTERM, который не может быть пойман с Python. (там я бы предпочел называть ОС._exit как предложил Хельмут)

Что делать, если я действительно хочу выйти из программы из потока (не то, что я на самом деле хотите, но только так я понимаю)?

мой предпочтительный метод-передача сообщений Erlang-ish. Немного simlified, я делаю это так:

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

Что делать, если я действительно хочу выйти из программы из потока?

помимо описанного метода Deestan вы можете вызвать os._exit (обратите внимание на подчеркивание). Перед его использованием убедитесь, что вы понимаете, что он делает нет очистка (например, вызов __del__ или подобные).

является ли тот факт, что "pre main exit, post thread exit" печатается, что вас беспокоит?

в отличие от некоторых других языков (например, Java), где аналог sys.exit (System.exit, в случае Java) заставляет VM / process / interpreter немедленно останавливаться, Python sys.exit просто выдает исключение: исключение SystemExit в частности.

вот документы для sys.exit (всего print sys.exit.__doc__):

выйти из интерпретатора путем повышения SystemExit(статус).
Если статус опущен или отсутствует, он по умолчанию равен нулю (т. е. успех).
Если состояние числовое, оно будет использоваться в качестве состояния выхода из системы.
Если это другой вид объекта, он будет напечатан и система
статус выхода будет один (т. е. сбой).

это имеет несколько последствий:

  • в потоке он просто убивает текущий поток, а не весь процесс (предполагая, что он получает все путь к вершине стека...)
  • деструктор объекта (__del__) потенциально вызываются как кадры стека, которые ссылаются на эти объекты разматываются
  • наконец блоки выполняются по мере разматывания стека
  • вы можете поймать SystemExit исключение

последнее, возможно, самое удивительное, и это еще одна причина, почему вы должны почти никогда не имеют безусловного except оператор в вашем коде Python.