[英]How to monitor a python process and restart it upon abnormal termination
[英]How to handle abnormal child process termination?
我正在使用 python 3.7 並遵循此文檔。 我想要一個進程,它應該產生一個子進程,等待它完成任務,並獲取一些信息。 我使用以下代碼:
if __name__ == '__main__':
q = Queue()
p = Process(target=some_func, args=(q,))
p.start()
print q.get()
p.join()
當子進程正確完成時,沒有問題,並且效果很好,但是當我的子進程在完成之前終止時,問題就開始了。 在這種情況下,我的應用程序正在等待。
給q.get()
和p.join()
一個超時並不能完全解決這個問題,因為我想立即知道子進程死了而不是等待超時。
另一個問題是q.get()
上的超時會產生異常,我更願意避免這種情況。
有人可以建議我一種更優雅的方法來克服這些問題嗎?
隊列和信號
一種可能性是注冊信號處理程序並使用它來傳遞哨兵值。 在 Unix 上,您可以在父級中處理SIGCHLD
,但這不是您的情況。 根據信號模塊:
在 Windows 上,signal() 只能用 SIGABRT、SIGFPE、SIGILL、SIGINT、SIGSEGV、SIGTERM 或 SIGBREAK 調用。
不確定通過任務管理器殺死它是否會轉化為SIGTERM
但您可以嘗試一下。
要處理SIGTERM
您需要在子SIGTERM
中注冊信號處理程序。
import os
import sys
import time
import signal
from functools import partial
from multiprocessing import Process, Queue
SENTINEL = None
def _sigterm_handler(signum, frame, queue):
print("received SIGTERM")
queue.put(SENTINEL)
sys.exit()
def register_sigterm(queue):
global _sigterm_handler
_sigterm_handler = partial(_sigterm_handler, queue=queue)
signal.signal(signal.SIGTERM, _sigterm_handler)
def some_func(q):
register_sigterm(q)
print(os.getpid())
for i in range(30):
time.sleep(1)
q.put(f'msg_{i}')
if __name__ == '__main__':
q = Queue()
p = Process(target=some_func, args=(q,))
p.start()
for msg in iter(q.get, SENTINEL):
print(msg)
p.join()
示例輸出:
12273
msg_0
msg_1
msg_2
msg_3
received SIGTERM
Process finished with exit code 0
即使這適用於任務管理器,您的用例聽起來也不能排除強制殺死,所以我認為您最好采用不依賴信號的方法。
如果您的進程p.is_alive()
調用queue.get()
並指定timeout
並處理Empty
異常,則您可以檢查循環:
import os
import time
from queue import Empty
from multiprocessing import Process, Queue
def some_func(q):
print(os.getpid())
for i in range(30):
time.sleep(1)
q.put(f'msg_{i}')
if __name__ == '__main__':
q = Queue()
p = Process(target=some_func, args=(q,))
p.start()
while p.is_alive():
try:
msg = q.get(timeout=0.1)
except Empty:
pass
else:
print(msg)
p.join()
也可以避免異常,但我不建議這樣做,因為您不會將等待時間花在“排隊”上,因此會降低響應速度:
while p.is_alive():
if not q.empty():
msg = q.get_nowait()
print(msg)
time.sleep(0.1)
如果您打算為每個孩子使用一個連接,則可以使用管道而不是隊列。 它比隊列(安裝在管道頂部)性能更高,您可以使用multiprocessing.connection.wait
(Python 3.3+) 一次等待多個對象准備就緒。
multiprocessing.connection.wait(object_list,超時=無)
等到 object_list 中的對象准備就緒。 返回 object_list 中准備好的對象的列表。 如果 timeout 是一個浮點數,那么調用最多會阻塞多少秒。 如果 timeout 為 None ,那么它將無限期地阻塞。 負超時相當於零超時。
對於 Unix 和 Windows,如果對象是可讀的 Connection 對象,則該對象可以出現在 object_list 中; 一個已連接且可讀的 socket.socket 對象; 或 Process 對象的哨兵屬性。 當有數據可供讀取或另一端已關閉時,連接或套接字對象已准備就緒。
Unix : wait(object_list, timeout) 幾乎等同於 select.select(object_list, [], [], timeout)。 不同之處在於,如果 select.select() 被信號中斷,它會引發錯誤號為 EINTR 的 OSError,而 wait() 不會。
Windows :object_list 中的項必須是可等待的整數句柄(根據 Win32 函數 WaitForMultipleObjects() 的文檔使用的定義),或者它可以是具有 fileno() 方法的對象,該方法返回套接字句柄或管柄。 (請注意,管道句柄和套接字句柄不是可等待句柄。)
您可以使用它來同時等待進程的哨兵屬性和管道的父端。
import os
import time
from multiprocessing import Process, Pipe
from multiprocessing.connection import wait
def some_func(conn_write):
print(os.getpid())
for i in range(30):
time.sleep(1)
conn_write.send(f'msg_{i}')
if __name__ == '__main__':
conn_read, conn_write = Pipe(duplex=False)
p = Process(target=some_func, args=(conn_write,))
p.start()
while p.is_alive():
wait([p.sentinel, conn_read]) # block-wait until something gets ready
if conn_read.poll(): # check if something can be received
print(conn_read.recv())
p.join()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.