Tại sao sys.exit () không thoát khi được gọi bên trong một luồng trong Python?


98

Đây có thể là một câu hỏi ngu ngốc, nhưng tôi đang thử nghiệm một số giả định của mình về Python và tôi bối rối không hiểu tại sao đoạn mã sau sẽ không thoát khi được gọi trong chuỗi, nhưng sẽ thoát khi được gọi trong chuỗi chính.

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"

Các tài liệu cho sys.exit () nói rằng lệnh gọi sẽ thoát khỏi Python. Tôi có thể thấy từ đầu ra của chương trình này rằng "bài thoát luồng" không bao giờ được in, nhưng luồng chính vẫn tiếp tục ngay cả sau khi thoát lệnh gọi luồng.

Có phải một phiên bản trình thông dịch riêng biệt đang được tạo cho mỗi luồng và lệnh gọi exit () chỉ thoát khỏi phiên bản riêng biệt đó không? Nếu vậy, việc triển khai phân luồng quản lý quyền truy cập vào tài nguyên được chia sẻ như thế nào? Điều gì sẽ xảy ra nếu tôi muốn thoát khỏi chương trình khỏi chuỗi (không phải tôi thực sự muốn, nhưng chỉ để tôi hiểu)?

Câu trả lời:


71

sys.exit()tăng SystemExitngoại lệ, cũng như vậy thread.exit(). Vì vậy, khi sys.exit()tăng ngoại lệ đó bên trong luồng đó, nó có tác dụng giống như gọi thread.exit(), đó là lý do tại sao chỉ luồng đó thoát ra.


23

Điều gì sẽ xảy ra nếu tôi muốn thoát khỏi chương trình khỏi chuỗi?

Ngoài phương thức mà Deestan đã mô tả, bạn có thể gọi os._exit(chú ý dấu gạch dưới). Trước khi sử dụng, hãy đảm bảo rằng bạn hiểu rằng nó không có thao tác dọn dẹp (như gọi điện __del__hoặc tương tự).


2
Nó sẽ tuôn ra I / O?
Lorenzo Belli

1
os._exit (n): "Thoát quá trình với trạng thái n, không gọi trình xử lý dọn dẹp, xả bộ đệm stdio, v.v."
Tim Richardson

Lưu ý rằng khi os._exitđược sử dụng trong lời nguyền, bảng điều khiển không được đặt lại về trạng thái bình thường với điều này. Bạn phải thực thi resettrong Unix-shell để sửa lỗi này.
sjngm 19/07/19

22

Điều gì sẽ xảy ra nếu tôi muốn thoát khỏi chương trình khỏi chuỗi (không phải tôi thực sự muốn, nhưng chỉ để tôi hiểu)?

Ít nhất trên Linux bạn có thể làm:

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

Điều này sẽ gửi một SIGINTđến chuỗi chính làm tăng mộtKeyboardInterrupt . Cùng với đó bạn có một sự dọn dẹp thích hợp. Bạn thậm chí có thể xử lý tín hiệu nếu bạn muốn.

BTW: Trên Windows, bạn chỉ có thể gửi một SIGTERMtín hiệu, không thể bắt được tín hiệu từ Python. Trong trường hợp đó, bạn có thể chỉ cần sử dụng os._exitvới hiệu ứng tương tự.


1
Cũng làm việc với lời nguyền không giống như os._exit
sjngm

12

Thực tế là "pre main exit, post thread exit" được in ra có làm phiền bạn không?

Không giống như một số ngôn ngữ khác (như Java) nơi tương tự với sys.exit( System.exit, trong trường hợp của Java) khiến máy ảo / tiến trình / trình thông dịch dừng ngay lập tức, Pythonsys.exit chỉ ném ra một ngoại lệ: cụ thể là một ngoại lệ SystemExit.

Đây là tài liệu cho sys.exit(chỉ print sys.exit.__doc__):

Thoát trình thông dịch bằng cách nâng SystemExit (trạng thái).
Nếu trạng thái bị bỏ qua hoặc Không có, nó sẽ mặc định là 0 (tức là thành công).
Nếu trạng thái là số, nó sẽ được sử dụng làm trạng thái thoát hệ thống.
Nếu nó là một loại đối tượng khác, nó sẽ được in ra và
trạng thái thoát hệ thống sẽ là một (tức là thất bại).

Điều này có một số hậu quả:

  • trong một luồng, nó chỉ giết luồng hiện tại, không phải toàn bộ quá trình (giả sử nó đi đến đầu ngăn xếp ...)
  • đối tượng hủy (__del__ ) có khả năng được gọi khi các khung ngăn xếp tham chiếu đến các đối tượng đó không được liên kết
  • cuối cùng các khối được thực thi khi ngăn xếp mở ra
  • bạn có thể bắt gặp một SystemExitngoại lệ

Điều cuối cùng có thể là điều đáng ngạc nhiên nhất, và là một lý do khác tại sao bạn hầu như không bao giờ nên có một exceptcâu lệnh không đủ tiêu chuẩn trong mã Python của mình.


11

Điều gì sẽ xảy ra nếu tôi muốn thoát khỏi chương trình khỏi chuỗi (không phải tôi thực sự muốn, nhưng chỉ để tôi hiểu)?

Phương pháp ưa thích của tôi là truyền thông điệp Erlang-ish. Mô phỏng một chút, tôi làm như thế này:

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

3
Bạn không cần một Queueở đây. Chỉ cần một đơn giản boolsẽ làm tốt. Tên cổ điển cho biến này là is_activevà giá trị mặc định ban đầu của nó là True.
Acumenus

3
Ư, bạn đung. Theo effbot.org/zone/thread-synchronization.htm , việc sửa đổi một bool(hoặc bất kỳ hoạt động nguyên tử nào khác) sẽ giải quyết được vấn đề cụ thể này một cách hoàn hảo. Lý do tôi đi với Queues là khi làm việc với các đại lý ren Tôi có xu hướng kết thúc cần tín hiệu khác nhau ( flush, reconnect, exit, vv ...) gần như ngay lập tức.
Deestan
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.