[英]Python multiprocessing.Queue apparently losing data
我正在嘗試利用多處理來加速程序。 為此,我在某些時候需要在盡可能多的進程之間並行化任務,比方說 n。 因為我不想創建超出絕對必要的進程,所以我創建了 n-1 個新進程,啟動它們,然后在當前進程上運行最后的工作,最后將所有內容連接在一起。 所有這些都通過隊列進行通信。 每個進程都通過參數傳遞其“工作份額”,因此每個進程只需要在完成后將結果放入隊列(每個結果的長度約為 6600 個 5 個字母的單詞)。
def play(chosen_word):
l=[chosen_word, chosen_word]
return l
def partial_test(id, words, queue):
print(f'Process {id} started and allocated {len(words)} words.')
guesses=[]
for word in words:
guesses.append(play(word))
print(f"Process {id} has finished ALL WORDS.") #debugging only
queue.put((id, guesses))
print(f'Process {id} added results to queue')
queue.cancel_join_thread()
print(f'Process {id} closed the queue and exited. Queue has aproximately {queue.qsize()} elements')
def full_test():
#do stuff
#create Queue for results
queue=Queue()
#initialize auxiliary processes
processes=[Process(target=partial_test, args=(x, word_list[x*words_per_process:(x+1)*words_per_process], queue)) for x in range(process_count-1)]
#start processes
for process in processes:
process.start()
#run last process on the current thread
partial_test(process_count-1, word_list[(process_count-1)*words_per_process:], queue)
#join processes
i=0
for process in processes:
process.join()
print(f'Joined process {i} with main thread.')
i+=1
print("All processes finished!")
#get results (they need to be in order)
results=[[] for _ in range(process_count)]
i=0
while not queue.empty():
res=queue.get()
results[res[0]]=res[1].copy()
i+=1
print(f"Got {i} results!")
#do stuff with results
當我嘗試讀取隊列中的數據時出現問題。 每個進程都報告說它把數據放到了隊列中,所以在最后一個加入之前它有 n 個元素。 但是,當我嘗試獲取它們並將它們放入結果列表時,我只提取了一個元素,其中包含第 n 個進程(我在主線程上運行的進程)記錄的數據。
我最初沒有使用 queue.cancel_join_thread(),但發現為了防止進程加入,即使在完成執行后,它們也會等待緩沖區實際寫入隊列,在大量的情況下數據,在調用 queue.get() 方法之前不會這樣做。 但是因為我只在所有進程完成后才獲取數據,所以永遠不會調用它並且程序會卡住。 我想這可能與此有關(盡管我不明白為什么它不會影響第 n 個進程),但我發現沒有辦法將緩沖區中的數據“強制刷新”到隊列中。
我也確信這部分代碼可能依賴於其他所有 function 返回正確的數據,因為我已經在單進程版本中測試了相同數據的所有內容。
編輯:劇本 function 只是一個替身,但就本文的所有意圖和目的而言,它與原始劇本等同,因為使用它會產生完全相同的問題。 發布原始代碼及其所有依賴項意味着發布我的大部分代碼,這會使我很難集中精力解決問題。
所以,這是你的問題:
我最初沒有使用 queue.cancel_join_thread(),但發現為了防止進程加入,即使在完成執行后,它們也會等待緩沖區實際寫入隊列,在大量的情況下數據,在調用 queue.get() 方法之前不會這樣做。 但是因為我只在所有進程完成后才獲取數據,所以永遠不會調用它並且程序會卡住。 我想這可能與此有關(盡管我不明白為什么它不會影響第 n 個進程),但我發現沒有辦法將緩沖區中的數據“強制刷新”到隊列中。
使用multiprocessing.Queue
,讀者需要在作者寫作的同時閱讀。 隊列是在有界大小的操作系統級管道之上實現的,如果沒有任何內容正在讀取,寫入者將很快填滿管道的緩沖區並變得無法寫入。 你得到的錯誤是由於那個。 您沒有找到“強制刷新”選項,因為沒有地方可以刷新 - pipe 已滿,沒有人正在閱讀以清除它。
cancel_join_thread
不能解決問題。 通過調用cancel_join_thread
,你是在告訴 Python,“不,我真的完全同意你扔掉我的數據” ,所以工作進程愉快地退出而沒有完成對 pipe 的寫入,並扔掉你的數據。 該文檔明確警告說,只有在您不關心丟失的數據時才應使用cancel_join_thread
。
您可以嘗試使用帶有manager = multiprocessing.Manager()
和queue = manager.Queue()
的托管隊列,因為 IIRC,托管隊列沒有相同的限制,但它們避免此限制的方式涉及創建額外的服務器進程管理隊列,按照您的方式設計程序的全部目的是避免創建額外的進程。 另外,我認為基於管理器的隊列有額外的進程間通信開銷。
我建議只使用multiprocessing.Queue
的設計使用方式 - 在寫入的同時讀取它。 不要將主進程用作額外的工作人員,而是讓它在完成啟動工作人員后立即開始讀取數據,並且只有在讀取所有數據后才加入工作人員。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.