[英]Python multiprocessing, parent process hangs at recv after child process raises exception before send
Python 絕對是並行處理最差的語言之一。
只是讓父進程在子進程失敗時拋出錯誤。 相反,父進程在recv()
方法處掛起。
子進程中的一些代碼:
try:
condition.acquire()
x = 0/0
pipeConn2.send('test data')
condition.notify_all()
condition.release()
except:
pipeConn2.close() # expect this to raise exception in parent process per docs
condition.notify_all()
condition.release()
父進程中的一些代碼:
try:
condition0.acquire()
condition0.wait()
pipe0Conn1.recv() # hangs here instead of raising exception
except:
# handle exception
首先,你有一個競爭條件。 即使您消除了除零異常並且從不關閉連接,如果子進程首先獲取與底層condition
實例關聯的隱式鎖,則主進程中對recv
的調用無論如何都會掛起(我給你懷疑condition
和condition0
確實引用了相同的實例。
問題是,如果您的子進程是第一個獲得鎖的,它將發送其數據,進行通知,釋放鎖並終止。 然后主進程將能夠第一次獲取鎖,然后調用wait
。 但是當它調用wait
時,它會釋放鎖並隱式等待已經發生的通知,並將無限期地掛起wait
。 您可以通過注釋掉語句x = 0/0
並插入import time; time.sleep(.5)
import time; time.sleep(.5)
作為主進程的try
塊中的第一條語句,以確保子進程首先獲取條件鎖。
只是為了解決這個問題,我使用一個Event
實例來暫停子進程,直到主進程向它發出信號,它已經獲得了條件鎖。
我已經驗證,當子進程關閉連接時,主進程掛起似乎確實存在問題。 但是,當我用多線程代替多處理時,這確實可以正常工作。 所以 Python 中似乎存在一個錯誤。 我已經在 Windows 和 Python 3.8.5 和 Linux 和 ZA7F5F35426B923784711Z 上測試了這個。
這是一個最小的、可重現的示例:
USE_THREADING = False
from multiprocessing import Pipe
if USE_THREADING:
from threading import Thread as SubTask, Condition, Event
else:
from multiprocessing import Process as SubTask, Condition, Event
def foo(condition, pipeConn2, acquired_event):
try:
acquired_event.wait()
condition.acquire()
x = 0/0
pipeConn2.send('test data')
except:
pipeConn2.close() # expect this to raise exception in parent process per docs
finally:
condition.notify_all()
condition.release()
if __name__ == '__main__':
condition = Condition()
pipeConn1, pipeConn2 = Pipe()
# Ensure that our main process/thread is first to acquire the condition:
acquired_event = Event()
p = SubTask(target=foo, args=(condition, pipeConn2, acquired_event))
p.start()
try:
condition.acquire()
# Now allow the subprocess/subthread to proceed to try to acquire the lock:
acquired_event.set()
condition.wait()
print('Receiving data ...')
data = pipeConn1.recv()
print(f"Data received: '{data}'")
except Exception as e:
print('Exception:', type(e))
p.join()
印刷:
Receiving data ...
順便說一句,你的例子並不是一個特別現實的例子。 Pipe
本質上是一個單一的消費者/單一的生產者通信工具,除非你在它上面構建了很多“腳手架”,然后你還不如只使用其中一個Queue
變體。 實際上,一個進程或線程只會在連接上進行阻塞接收,您的生產者可以編寫一個特殊的標記object (通常None
足夠的)來表示這是“文件結尾”。 確實沒有必要對連接進行顯式關閉; 當它被垃圾收集時它將被關閉。
我遇到了完全相同的問題,即Parent Pipe Connection
正在無限期地等待。 我沒有使用Lock
,而是使用了Parent Pipe Connection. Poll
Parent Pipe Connection. Poll
function 並返回 boolean 值,具體取決於是否有任何接收。 一些例子:-
沒有Poll
-
try:
pipe0Conn1.recv() # Waits indefinetly (until it recieves)
except EOFError as e:
# handling not recieved
except:
# some other exception
沒有Poll
-
try:
if pipe0Conn1.poll() is True:
pipe0Conn1.recv() # Doesn't wait indefinetly (immediate recieve)
else:
# Some other code for handling nothing recieved
except EOFError as e:
# handling not recieved
except:
# some other exception
PS:這是我在stackoverflow上的第一個答案,對於任何語法、格式、代碼錯誤,我感到非常抱歉。 如果您願意,請評論任何反饋。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.