เหตุใด sys.exit () จึงไม่ออกเมื่อถูกเรียกภายในเธรดใน Python


101

นี่อาจเป็นคำถามโง่ ๆ แต่ฉันกำลังทดสอบสมมติฐานบางอย่างของฉันเกี่ยวกับ 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 ฉันสามารถเห็นได้จากผลลัพธ์ของโปรแกรมนี้ว่า "การออกจากเธรด" จะไม่ถูกพิมพ์ออกมา แต่เธรดหลักก็ยังคงทำงานต่อไปแม้หลังจากที่เธรดเรียกออก

มีการสร้างอินสแตนซ์แยกต่างหากของตัวแปลสำหรับแต่ละเธรดและการเรียกเพื่อออก () เป็นเพียงการออกจากอินสแตนซ์แยกนั้นหรือไม่ ถ้าเป็นเช่นนั้นการใช้งานเธรดจะจัดการการเข้าถึงทรัพยากรที่แชร์ได้อย่างไร จะเป็นอย่างไรถ้าฉันต้องการออกจากโปรแกรมจากเธรด (ไม่ใช่ว่าฉันต้องการจริงๆ แต่ฉันก็เข้าใจ)

คำตอบ:


74

sys.exit()ยกข้อยกเว้นเช่นเดียวกับSystemExit thread.exit()ดังนั้นเมื่อsys.exit()เพิ่มข้อยกเว้นภายในเธรดนั้นจะมีผลเช่นเดียวกับการโทรthread.exit()ซึ่งเป็นสาเหตุที่เธรดออกเท่านั้น


25

จะเกิดอะไรขึ้นถ้าฉันต้องการออกจากโปรแกรมจากเธรด?

นอกเหนือจากวิธีการที่ Deestan อธิบายไว้คุณสามารถโทรได้os._exit(สังเกตขีดล่าง) ก่อนใช้ตรวจสอบให้แน่ใจว่าคุณเข้าใจว่าไม่มีการล้างข้อมูล (เช่นการโทร__del__หรือสิ่งที่คล้ายกัน)


2
จะล้าง I / O หรือไม่?
Lorenzo Belli

2
os._exit (n): "ออกจากกระบวนการด้วยสถานะ n โดยไม่ต้องเรียกตัวจัดการการล้างข้อมูลการล้างบัฟเฟอร์ stdio ฯลฯ "
Tim Richardson

โปรดทราบว่าเมื่อos._exitใดที่ใช้ภายในคำสาปคอนโซลจะไม่ถูกรีเซ็ตเป็นสถานะปกติด้วยสิ่งนี้ คุณต้องดำเนินการresetใน Unix-shell เพื่อแก้ไขปัญหานี้
sjngm

25

จะเกิดอะไรขึ้นถ้าฉันต้องการออกจากโปรแกรมจากเธรด?

สำหรับ Linux:

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

สิ่งนี้ส่งSIGINTไปยังเธรดหลักซึ่งทำให้เกิดไฟล์KeyboardInterrupt. ด้วยการที่คุณมีการล้างข้อมูลที่เหมาะสม นอกจากนี้คุณสามารถลงทะเบียนตัวจัดการได้หากคุณต้องการตอบสนองที่แตกต่างออกไป

ดังกล่าวข้างต้นไม่ทำงานบน Windows เป็นคุณเท่านั้นที่สามารถส่งสัญญาณที่ไม่ได้รับการจัดการโดยงูหลามและมีผลเช่นเดียวกับSIGTERMsys._exit()

สำหรับ Windows:

คุณสามารถใช้ได้:

sys._exit()

สิ่งนี้จะออกจากกระบวนการทั้งหมด อย่างไรก็ตามไม่มีการล้างข้อมูลใด ๆ หากคุณต้องการสิ่งนั้นคุณต้องสื่อสารกับเธรดหลักด้วยวิธีอื่น


2
ใช้งานได้กับคำสาปที่แตกต่างจากos._exit
sjngm

12

มีการพิมพ์ข้อความ "ก่อนออกหลักทางออกกระทู้" สิ่งที่รบกวนคุณหรือไม่?

ซึ่งแตกต่างจากภาษาอื่น ๆ (เช่น Java) ที่อะนาล็อกเป็นsys.exit( System.exitในกรณีของ Java) ทำให้ VM / process / interpreter หยุดทันที Python sys.exitเพียงแค่แสดงข้อยกเว้น: ข้อยกเว้น SystemExit โดยเฉพาะ

นี่คือเอกสารสำหรับsys.exit(just print sys.exit.__doc__):

ออกจากล่ามโดยเพิ่ม SystemExit (สถานะ)
หากสถานะถูกละไว้หรือไม่มีค่าเริ่มต้นจะเป็นศูนย์ (กล่าวคือสำเร็จ)
หากสถานะเป็นตัวเลขจะใช้เป็นสถานะการออกจากระบบ
หากเป็นวัตถุชนิดอื่นระบบจะพิมพ์และ
สถานะการออกจากระบบจะเป็นหนึ่ง (กล่าวคือความล้มเหลว)

สิ่งนี้มีผลกระทบเล็กน้อย:

  • ในเธรดมันเพียงแค่ฆ่าเธรดปัจจุบันไม่ใช่กระบวนการทั้งหมด (สมมติว่ามันเข้าสู่ด้านบนสุดของสแต็ก ... )
  • ตัวทำลายวัตถุ ( __del__) อาจถูกเรียกใช้เนื่องจากสแต็กเฟรมที่อ้างอิงอ็อบเจ็กต์เหล่านั้นคลายออก
  • ในที่สุดบล็อกจะถูกดำเนินการเมื่อสแต็กคลายตัว
  • คุณสามารถจับSystemExitข้อยกเว้น

ข้อสุดท้ายอาจเป็นเรื่องที่น่าประหลาดใจที่สุดและเป็นอีกสาเหตุหนึ่งที่คุณแทบไม่ควรมีexceptคำสั่งที่ไม่มีเงื่อนไขในรหัส Python ของคุณ


12

จะเป็นอย่างไรถ้าฉันต้องการออกจากโปรแกรมจากเธรด (ไม่ใช่ว่าฉันต้องการจริงๆ แต่ฉันก็เข้าใจ)

วิธีที่ฉันชอบคือการส่งข้อความ 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

4
คุณไม่จำเป็นต้องมีQueueที่นี่ ง่ายๆแค่boolจะทำดี ชื่อคลาสสิกสำหรับตัวแปรนี้เป็นและค่าเริ่มต้นเป็นครั้งแรกคือis_active True
Acumenus

4
ใช่คุณถูกต้อง. ตามeffbot.org/zone/thread-synchronization.htmการปรับเปลี่ยนbool(หรือการดำเนินการอะตอมอื่น ๆ ) จะทำได้อย่างสมบูรณ์แบบสำหรับปัญหานี้ เหตุผลที่ผมไปกับQueues คือว่าเมื่อทำงานร่วมกับตัวแทนเกลียวฉันมักจะจบลงด้วยการที่ต้องส่งสัญญาณที่แตกต่างกัน ( flush, reconnect, exitฯลฯ ... ) เกือบจะในทันที
Deestan

1
Deestan: เนื่องจากboolจะใช้งานได้ดังที่ @Acumenus ชี้ให้เห็นเช่นเดียวกันสิ่งง่ายๆintก็ดูเหมือนจะเป็นสิ่งที่จำเป็นเพื่อให้สามารถจัดการกับสัญญาณต่างๆได้ - boolเป็นเพียงคลาสย่อยของintหลังจากทั้งหมด
martineau
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.