ไม่สามารถฆ่าสคริปต์ Python ด้วย Ctrl-C


117

ฉันกำลังทดสอบ Python threading ด้วยสคริปต์ต่อไปนี้:

import threading

class FirstThread (threading.Thread):
    def run (self):
        while True:
            print 'first'

class SecondThread (threading.Thread):
    def run (self):
        while True:
            print 'second'

FirstThread().start()
SecondThread().start()

สิ่งนี้กำลังทำงานใน Python 2.7 บน Kubuntu 11.10 Ctrl+ Cจะไม่ฆ่ามัน ฉันลองเพิ่มตัวจัดการสำหรับสัญญาณระบบด้วย แต่ไม่ได้ช่วยอะไร:

import signal 
import sys
def signal_handler(signal, frame):
    sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)

ในการฆ่ากระบวนการฉันกำลังฆ่ามันโดย PID หลังจากส่งโปรแกรมไปที่พื้นหลังด้วยCtrl+ Zซึ่งจะไม่ถูกละเลย ทำไมCtrl+ ถึงCถูกเพิกเฉยอย่างต่อเนื่อง? ฉันจะแก้ไขปัญหานี้ได้อย่างไร


@dotancohen มันทำงานบน Windows หรือไม่?
kiriloff

@vitaibian: ฉันไม่ได้ทดสอบบน Windows แต่ดูเหมือนว่าจะไม่ใช่ระบบปฏิบัติการที่เฉพาะเจาะจง
dotancohen

คำตอบ:


178

Ctrl+ Cยุติเธรดหลัก แต่เนื่องจากเธรดของคุณไม่ได้อยู่ในโหมด daemon จึงทำงานต่อไปและทำให้กระบวนการมีชีวิตอยู่ เราสามารถทำให้พวกมันเป็นภูตได้:

f = FirstThread()
f.daemon = True
f.start()
s = SecondThread()
s.daemon = True
s.start()

แต่แล้วก็มีปัญหาอีกครั้ง - เมื่อเธรดหลักเริ่มต้นเธรดของคุณแล้วจะไม่มีอะไรให้ทำอีก ดังนั้นมันจะออกและเธรดจะถูกทำลายทันที ดังนั้นขอให้เธรดหลักมีชีวิตอยู่:

import time
while True:
    time.sleep(1)

ตอนนี้ก็จะช่วยให้การพิมพ์ครั้งแรก 'และ 'สอง' จนกว่าคุณจะตี+CtrlC

แก้ไข:ตามที่ผู้แสดงความคิดเห็นได้ชี้ให้เห็นเธรด daemon อาจไม่มีโอกาสล้างสิ่งต่างๆเช่นไฟล์ชั่วคราว หากคุณต้องการสิ่งนั้นให้จับKeyboardInterruptบนเธรดหลักและให้ประสานการล้างข้อมูลและการปิดเครื่อง แต่ในหลาย ๆ กรณีการปล่อยให้เธรด daemon ตายอย่างกะทันหันก็น่าจะดีพอ


6
คุณควรพูดถึงว่าการทำเธรดนี้ไม่ได้หยุดลงอย่างสง่างามและทรัพยากรบางอย่างไม่ได้รับการเผยแพร่
Tommaso Barbugli

1
Ctrl-C ไม่เคยเป็นวิธีที่สง่างามในการหยุดสิ่งใด ๆ ฉันไม่แน่ใจว่าทรัพยากรอะไรจะเหลืออยู่ - ระบบปฏิบัติการไม่ควรเรียกคืนอะไรเลยเมื่อกระบวนการออกหรือไม่?
Thomas K

7
@ThomasK ไฟล์ชั่วคราวที่สร้างโดยtempfile.TemporaryFile()อาจถูกทิ้งไว้ในดิสก์ตัวอย่างเช่น
Feuermurmel

1
@date02392: ฉันไม่รู้ว่าเกิดอะไรขึ้นกับเธรดหลัก แต่เท่าที่ฉันรู้ว่าคุณไม่สามารถทำอะไรกับมันได้หลังจากออกไป กระบวนการจะเสร็จสิ้นเมื่อเธรดที่ไม่ใช่ daemon ทั้งหมดเสร็จสิ้น ความสัมพันธ์ระหว่างพ่อแม่ลูกไม่ได้เกิดขึ้น
Thomas K

4
ดูเหมือนว่าใน python3 คุณสามารถผ่านdaemon=Trueไปได้Thread.__init__
Ryan Haining


3

ฉันคิดว่าดีที่สุดที่จะเรียก join () ในเธรดของคุณเมื่อคุณคาดว่าพวกเขาจะตาย ฉันใช้เสรีภาพกับรหัสของคุณเพื่อทำให้ลูปสิ้นสุดลง (คุณสามารถเพิ่มสิ่งที่จำเป็นในการล้างข้อมูลได้เช่นกัน) ตัวแปร die จะถูกตรวจสอบความจริงในแต่ละรอบและเมื่อเป็น True โปรแกรมจะออก

import threading
import time

class MyThread (threading.Thread):
    die = False
    def __init__(self, name):
        threading.Thread.__init__(self)
        self.name = name

    def run (self):
        while not self.die:
            time.sleep(1)
            print (self.name)

    def join(self):
        self.die = True
        super().join()

if __name__ == '__main__':
    f = MyThread('first')
    f.start()
    s = MyThread('second')
    s.start()
    try:
        while True:
            time.sleep(2)
    except KeyboardInterrupt:
        f.join()
        s.join()

while Trueเป็นเรื่องงี่เง่าคุณควรjoinโดยตรง - และฟังก์ชันการลบล้างนั้นเป็นเรื่องที่น่าสงสัย บางทีdef join(self, force=False): if force: self.die = Trueนั่นอาจjoin()จะไม่เปลี่ยนแปลงโดยการjoin(force=True)ฆ่าพวกมัน แต่ถึงอย่างนั้นควรแจ้งทั้งสองเธรดก่อนที่จะเข้าร่วมอย่างใดอย่างหนึ่ง
o11c

0

คำตอบของ @Thomas K รุ่นปรับปรุง:

  • การกำหนดฟังก์ชันผู้ช่วยis_any_thread_alive()ตามส่วนสำคัญนี้ซึ่งสามารถยกเลิกการmain()ทำงานโดยอัตโนมัติ

ตัวอย่างรหัส:

import threading

def job1():
    ...

def job2():
    ...

def is_any_thread_alive(threads):
    return True in [t.is_alive() for t in threads]

if __name__ == "__main__":
    ...
    t1 = threading.Thread(target=job1,daemon=True)
    t2 = threading.Thread(target=job2,daemon=True)
    t1.start()
    t2.start()

    while is_any_thread_alive([t1,t2]):
        time.sleep(0)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.