Python'da bir iş parçacığı içinde çağrıldığında sys.exit () neden çıkmıyor?


101

Bu aptalca bir soru olabilir, ancak Python hakkındaki bazı varsayımlarımı test ediyorum ve aşağıdaki kod parçacığının iş parçacığında çağrıldığında neden çıkmayacağı, ancak ana iş parçacığında çağrıldığında çıkacağı konusunda kafam karıştı.

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 () için dokümanlar, çağrının Python'dan çıkması gerektiğini belirtir. Bu programın çıktısından "post thread exit" in asla yazdırılmadığını, ancak thread çağrıları çıktıktan sonra bile main thread devam ettiğini görebiliyorum.

Her iş parçacığı için yorumlayıcının ayrı bir örneği mi oluşturuluyor ve exit () çağrısı bu ayrı örnekten çıkıyor mu? Öyleyse, iş parçacığı uygulaması paylaşılan kaynaklara erişimi nasıl yönetiyor? Ya iş parçacığından programdan çıkmak isteseydim (aslında istediğimden değil, ama sadece anlayabilmek için)?

Yanıtlar:


74

sys.exit()SystemExitolduğu gibi istisnayı ortaya çıkarır thread.exit(). Bu nedenle, sys.exit()bu iş parçacığı içinde bu istisnayı yükselttiğinde, çağrı ile aynı etkiye sahiptir thread.exit(), bu nedenle yalnızca evre çıkar.


25

İş parçacığından programdan çıkmak istersem ne olur?

Deestan'ın tarif ettiği yöntemin dışında arayabilirsin os._exit(alt çizgiye dikkat edin). Kullanmadan önce temizleme olmadığını anladığınızdan emin olun (arama __del__veya benzeri gibi ).


2
I / O'yu yıkar mı?
Lorenzo Belli

2
os._exit (n): "İşlemden durum n ile, temizleme işleyicilerini çağırmadan, stdio arabelleklerini temizlemeden vb. çıkın."
Tim Richardson

Ne zaman unutmayın os._exitküfürler içinde kullanılan konsol bu normal bir duruma sıfırlanmaz. Bunu resetdüzeltmek için Unix kabuğunda çalıştırmanız gerekir .
sjngm

25

İş parçacığından programdan çıkmak istersem ne olur?

Linux için:

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

Bu, a'yı SIGINTyükselten ana iş parçacığına a gönderir KeyboardInterrupt. Bununla düzgün bir temizlik yaparsınız. Ayrıca, farklı tepki vermek istiyorsanız, bir işleyici kaydedebilirsiniz.

Yukarıdakiler Windows'ta çalışmaz, çünkü yalnızca SIGTERMPython tarafından işlenmeyen ve ile aynı etkiye sahip bir sinyal gönderebilirsiniz sys._exit().

Pencereler için:

Kullanabilirsiniz:

sys._exit()

Bu tüm süreçten çıkacak. Ancak, herhangi bir temizlik olmadan. Buna ihtiyacınız varsa, ana iş parçacığı ile başka bir şekilde iletişim kurmanız gerekir.


2
Ayrıca farklı küfürler ile çalışır os._exit
sjngm

12

Sizi rahatsız eden "ana çıkış öncesi, iş parçacığı çıkış sonrası" basılmış olması mı?

Analogun sys.exit( System.exitJava'nın durumunda) VM / işlem / yorumlayıcının hemen durmasına neden olduğu diğer bazı dillerin (Java gibi) aksine , Python sys.exitsadece bir istisna atar: özellikle bir Sistem Çıkışı istisnası.

İşte sys.exit(sadece print sys.exit.__doc__) için dokümanlar :

SystemExit'i (durum) yükselterek yorumlayıcıdan çıkın.
Durum atlanırsa veya Yok ise, varsayılan olarak sıfır olur (yani başarı).
Durum sayısal ise, sistem çıkış durumu olarak kullanılacaktır.
Başka tür bir nesne ise, yazdırılacak ve sistem
çıkış durumu bir olacaktır (yani, arıza).

Bunun birkaç sonucu vardır:

  • bir iş parçacığında, tüm süreci değil, yalnızca geçerli iş parçacığını öldürür (yığının en üstüne kadar ulaştığını varsayarsak ...)
  • Nesne yıkıcıları ( __del__), bu nesnelere başvuran yığın çerçeveleri çözülmediğinden potansiyel olarak çağrılır
  • son olarak yığın çözülürken bloklar yürütülür
  • bir SystemExitistisna yakalayabilirsin

Sonuncusu muhtemelen en şaşırtıcı olanıdır ve exceptPython kodunuzda neredeyse hiçbir zaman niteliksiz bir ifadeye sahip olmamanızın bir başka nedenidir .


12

Ya iş parçacığından programdan çıkmak isteseydim (aslında istediğimden değil, ama sadece anlayabilmek için)?

Tercih ettiğim yöntem Erlang-ish mesaj geçişidir. Biraz basitleştirilmiş, bunu şöyle yapıyorum:

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

4
Buraya ihtiyacınız Queueyok. Sadece basit bir booliş görür. Bu değişkenin klasik adı is_activeve ilk varsayılan değeri True.
Acumenus

4
Evet haklısın. Effbot.org/zone/thread-synchronization.htm'ye göre , bir bool(veya başka bir atomik işlemi) değiştirmek bu özel problem için mükemmel bir iş çıkaracaktır. Ben gitmek nedeni Queues dişli ajanlar ile çalışırken birkaç farklı sinyaller ihtiyacı kadar sona eğiliminde olmasıdır ( flush, reconnect, exit, vs ...) neredeyse hemen.
Deestan

1
Deestan: bool@Acumenus'un belirttiği gibi a çalışacağına göre, aynı şekilde intbirkaç farklı sinyali idare edebilmek için gereken tek şey basit gibi görünüyor - sonuçta boolsadece bir alt sınıftır int.
martineau
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.