Mengapa sys.exit () tidak keluar saat dipanggil di dalam utas dengan Python?

104

Ini bisa menjadi pertanyaan yang bodoh, tetapi saya menguji beberapa asumsi saya tentang Python dan saya bingung mengapa potongan kode berikut tidak akan keluar saat dipanggil di utas, tetapi akan keluar saat dipanggil di utas utama.

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"

Dokumen untuk sys.exit () menyatakan bahwa panggilan harus keluar dari Python. Saya dapat melihat dari output program ini bahwa "post thread exit" tidak pernah dicetak, tetapi thread utama terus berjalan bahkan setelah thread memanggil keluar.

Apakah instance terpisah dari interpreter sedang dibuat untuk setiap thread, dan panggilan ke exit () hanya keluar dari instance terpisah itu? Jika ya, bagaimana implementasi threading mengelola akses ke sumber daya bersama? Bagaimana jika saya memang ingin keluar dari program dari utas (bukan yang sebenarnya saya inginkan, tetapi supaya saya mengerti)?

Shabbyrobe
sumber

Jawaban:

75

sys.exit()memunculkan SystemExitpengecualian, seperti halnya thread.exit(). Jadi, ketika sys.exit()memunculkan pengecualian itu di dalam utas itu, itu memiliki efek yang sama seperti memanggil thread.exit(), itulah sebabnya hanya utas yang keluar.

rpkelly
sumber
26

Bagaimana jika saya memang ingin keluar dari program dari utas?

Untuk Linux:

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

Ini mengirimkan a SIGINTke utas utama yang memunculkan a KeyboardInterrupt. Dengan itu Anda memiliki pembersihan yang layak. Anda juga dapat mendaftarkan seorang penangan, jika Anda ingin bereaksi secara berbeda.

Hal di atas tidak berfungsi pada Windows, karena Anda hanya dapat mengirim SIGTERMsinyal, yang tidak ditangani oleh Python dan memiliki efek yang sama seperti sys._exit().

Untuk Windows:

Kamu dapat memakai:

sys._exit()

Ini akan keluar dari seluruh proses. Namun, tanpa pembersihan apapun. Jika Anda membutuhkannya, Anda perlu berkomunikasi dengan utas utama dengan cara lain.

Chris
sumber
2
Juga bekerja dengan kutukan tidak seperti os._exit
sjngm
25

Bagaimana jika saya memang ingin keluar dari program dari utas?

Terlepas dari metode yang dijelaskan Deestan, Anda dapat memanggil os._exit(perhatikan garis bawahnya ). Sebelum menggunakannya pastikan bahwa Anda memahami bahwa hal itu tidak ada pembersihan (seperti menelepon __del__atau serupa).

Helmut Grohne
sumber
2
Apakah ini akan menghilangkan I / O?
Lorenzo Belli
2
os._exit (n): "Keluar dari proses dengan status n, tanpa memanggil penangan pembersihan, membersihkan buffer stdio, dll."
Tim Richardson
Perhatikan bahwa ketika os._exitdigunakan dalam kutukan konsol tidak diatur ulang ke keadaan normal dengan ini. Anda harus mengeksekusi resetdi Unix-shell untuk memperbaikinya.
sjngm
12

Apakah fakta bahwa "pre main exit, post thread exit" dicetak yang mengganggu Anda?

Tidak seperti beberapa bahasa lain (seperti Java) di mana analog ke sys.exit( System.exit, dalam kasus Java) menyebabkan VM / proses / interpreter segera berhenti, Python sys.exithanya melontarkan pengecualian: khususnya pengecualian SystemExit.

Berikut adalah dokumen untuk sys.exit(hanya print sys.exit.__doc__):

Keluar dari interpreter dengan menaikkan SystemExit (status).
Jika status dihilangkan atau Tidak ada, defaultnya ke nol (yaitu, berhasil).
Jika statusnya numerik, itu akan digunakan sebagai status keluar sistem.
Jika itu adalah jenis objek lain, itu akan dicetak dan
status keluar sistem akan menjadi satu (yaitu, gagal).

Ini memiliki beberapa konsekuensi:

  • dalam sebuah utas, itu hanya membunuh utas saat ini, bukan seluruh proses (dengan asumsi itu sampai ke puncak tumpukan ...)
  • object destructors ( __del__) berpotensi dipanggil saat tumpukan frame yang mereferensikan objek tersebut dibatalkan
  • akhirnya blok dieksekusi saat tumpukan terlepas
  • Anda bisa menangkap SystemExitpengecualian

Yang terakhir mungkin yang paling mengejutkan, dan merupakan alasan lain mengapa Anda hampir tidak pernah memiliki exceptpernyataan yang tidak memenuhi syarat dalam kode Python Anda.

Laurence Gonsalves
sumber
12

Bagaimana jika saya memang ingin keluar dari program dari utas (bukan yang sebenarnya saya inginkan, tetapi supaya saya mengerti)?

Metode pilihan saya adalah penyampaian pesan Erlang-ish. Sedikit disederhanakan, saya melakukannya seperti ini:

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
sumber
4
Anda tidak perlu di Queuesini. Sederhana boolsaja sudah cukup. Nama klasik untuk variabel ini adalah is_activedan nilai default awalnya adalah True.
Acumenus
4
Ya kamu benar. Menurut effbot.org/zone/thread-synchronization.htm , memodifikasi a bool(atau operasi atom lainnya) akan berhasil dengan sempurna untuk masalah khusus ini. Alasan saya pergi dengan Queues adalah bahwa ketika bekerja dengan agen berulir saya cenderung berakhir membutuhkan beberapa sinyal yang berbeda ( flush, reconnect, exit, dll ...) segera.
Deestan
1
Deestan: Karena a boolakan berhasil, seperti yang @Acumenus tunjukkan, maka yang sederhana juga intakan menjadi satu -satunya yang dibutuhkan untuk dapat menangani beberapa sinyal yang berbeda - bagaimanapun booljuga, hanyalah subclass dari int.
martineau
0

_thread.interrupt_main() tersedia sejak Python 3.7 (opsional sebelum itu)

psorenson
sumber