Không thể giết tập lệnh Python bằng Ctrl-C


117

Tôi đang kiểm tra luồng Python với đoạn script sau:

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()

Điều này đang chạy trong Python 2.7 trên Kubfox 11.10. Ctrl+ Csẽ không giết nó. Tôi cũng đã thử thêm một trình xử lý cho các tín hiệu hệ thống, nhưng điều đó không giúp ích gì:

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

Để giết tiến trình, tôi sẽ giết nó bằng cách sau khi gửi chương trình xuống nền với Ctrl+ Z, không bị bỏ qua. Tại sao Ctrl+ Cbị bỏ qua liên tục như vậy? Làm thế nào tôi có thể giải quyết điều này?


@dotancohen nó có hoạt động trên Windows không?
kiriloff

@vitaibian: Tôi chưa thử nghiệm trên Windows, nhưng có vẻ như không phải hệ điều hành cụ thể.
dotancohen

Câu trả lời:


178

Ctrl+ Cchấm dứt luồng chính, nhưng vì các luồng của bạn không ở chế độ trình nền, nên chúng tiếp tục chạy và điều đó giữ cho quá trình tồn tại. Chúng ta có thể làm cho chúng daemon:

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

Nhưng sau đó, có một vấn đề khác - một khi chủ đề chính đã bắt đầu các chủ đề của bạn, không có gì khác để nó làm. Vì vậy, nó thoát ra, và các chủ đề bị phá hủy ngay lập tức. Vì vậy, hãy giữ cho chủ đề chính còn sống:

import time
while True:
    time.sleep(1)

Bây giờ nó sẽ tiếp tục in 'đầu tiên' và 'thứ hai' cho đến khi bạn nhấn Ctrl+ C.

Chỉnh sửa: như các nhà bình luận đã chỉ ra, các chủ đề daemon có thể không có cơ hội để dọn dẹp những thứ như các tập tin tạm thời. Nếu bạn cần điều đó, sau đó nắm bắt KeyboardInterruptchủ đề chính và điều phối nó dọn dẹp và tắt máy. Nhưng trong nhiều trường hợp, để cho các chủ đề daemon đột ngột chết có lẽ là đủ tốt.


6
bạn nên đề cập rằng bằng cách thực hiện các chủ đề này không được dừng lại một cách duyên dáng và một số tài nguyên không được phát hành.
Tommaso Barbugli

1
Chà, Ctrl-C không bao giờ là một cách duyên dáng để ngăn chặn bất cứ điều gì. Tôi không chắc chắn những tài nguyên nào sẽ còn lại - hệ điều hành không đòi lại bất cứ thứ gì khi quá trình thoát?
Thomas K

7
@ThomasK Các tệp tạm thời được tạo bởi tempfile.TemporaryFile()có thể được để lại trên đĩa, ví dụ.
Feuermurmel

1
@ deed02392: Tôi không biết chính xác điều gì xảy ra với luồng chính, nhưng theo tôi biết bạn không thể làm gì với nó sau khi nó thoát. Quá trình sẽ kết thúc khi tất cả các luồng không phải daemon đã kết thúc; mối quan hệ cha-con không đi vào điều đó.
Thomas K

4
Trông giống như trong python3 bạn có thể vượt quadaemon=True đểThread.__init__
Ryan Haining


3

Tôi nghĩ rằng tốt nhất là gọi tham gia () trên các chủ đề của bạn khi bạn mong họ chết. Tôi đã thực hiện một số quyền tự do với mã của bạn để làm cho các vòng lặp kết thúc (bạn cũng có thể thêm bất kỳ nhu cầu dọn dẹp nào được yêu cầu ở đó). Biến chết được kiểm tra sự thật trên mỗi lần vượt qua và khi đó là True thì chương trình sẽ thoát.

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 Truelà ngớ ngẩn, bạn nên jointrực tiếp - và chức năng overriden đó là loại nghi vấn. Có lẽ def join(self, force=False): if force: self.die = Truevì thế mà join()không thay đổi bằng cách join(force=True)giết chết họ. Nhưng ngay cả sau đó, tốt hơn là thông báo cho cả hai chủ đề trước khi tham gia một trong hai.
o11c

0

Phiên bản cải tiến của câu trả lời của @Thomas K:

  • Xác định chức năng trợ lý is_any_thread_alive()theo ý chính này , có thể chấm dứt main()tự động.

Mã ví dụ:

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.