[英]error_callback in multiprocessing.Pool apply_async in Python 2?
[英]multiprocessing.Pool: calling helper functions when using apply_async's callback option
apply_async
的流程如何在調用iterable(?)函數和回調函數之間起作用?
設置:我正在讀取2000文件目錄中的所有文件的一些行,一些有數百萬行,有些只有少數幾行。 提取一些標題/格式/日期數據以對每個文件進行特征化。 這是在16 CPU機器上完成的,因此對它進行多處理是有意義的。
目前,預期的結果被發送到一個列表( ahlala
),所以我可以打印出來; 之后,這將被寫入* .csv。 這是我的代碼的簡化版本,最初基於這個非常有用的帖子。
import multiprocessing as mp
def dirwalker(directory):
ahlala = []
# X() reads files and grabs lines, calls helper function to calculate
# info, and returns stuff to the callback function
def X(f):
fileinfo = Z(arr_of_lines)
return fileinfo
# Y() reads other types of files and does the same thing
def Y(f):
fileinfo = Z(arr_of_lines)
return fileinfo
# results() is the callback function
def results(r):
ahlala.extend(r) # or .append, haven't yet decided
# helper function
def Z(arr):
return fileinfo # to X() or Y()!
for _,_,files in os.walk(directory):
pool = mp.Pool(mp.cpu_count()
for f in files:
if (filetype(f) == filetypeX):
pool.apply_async(X, args=(f,), callback=results)
elif (filetype(f) == filetypeY):
pool.apply_async(Y, args=(f,), callback=results)
pool.close(); pool.join()
return ahlala
注意,如果我將所有Z()
(輔助函數)放入X()
, Y()
或results()
,代碼就可以工作,但這是重復還是可能慢? 我知道每個函數調用都會調用回調函數,但是何時調用回調函數? 是在pool.apply_async()
...完成進程的所有作業? 如果在第一個函數pool.apply_async()
的范圍(?)內調用這些輔助函數,那么它應該更快嗎? pool.apply_async()
需要(在這種情況下, X()
)? 如果沒有,我應該把幫助函數放在results()
嗎?
其他相關的想法:守護進程是否為什么沒有出現? 我也很困惑如何排隊,如果這是問題。 這似乎是一個開始學習它的地方 ,但是在使用apply_async
,或者只是在顯着的時間效率低下,可以安全地忽略排隊嗎?
你在這里詢問了很多不同的東西,所以我會盡力覆蓋它:
一旦工作進程返回其結果,您傳遞給callback
的函數將在主進程(而不是worker)中執行。 它在Pool
對象內部創建的線程中執行。 該線程使用result_queue
對象,該對象用於從所有工作進程獲取結果。 線程將結果從隊列中拉出后,它會執行callback
。 當您的回調正在執行時,不能從隊列中提取其他結果,因此回調快速完成非常重要。 在您的示例中,只要您通過apply_async
對X
或Y
進行的apply_async
完成,結果將由worker進程放入result_queue
,然后結果處理線程將結果從result_queue
,並且您的callback
將被執行。
其次,我懷疑你沒有看到你的示例代碼發生任何事情的原因是因為所有的工作函數調用都失敗了。 如果worker函數失敗,則永遠不會執行callback
。 除非您嘗試從apply_async
調用返回的AsyncResult
對象中獲取結果,否則根本不會報告失敗。 但是,由於您沒有保存任何這些對象,因此您永遠不會知道發生的故障。 如果我是你,我會在你測試時嘗試使用pool.apply
,這樣你就會在發生錯誤時立即看到錯誤。
工人可能失敗的原因(至少在你提供的示例代碼中)是因為X
和Y
被定義為另一個函數內的函數。 multiprocessing
將函數和對象傳遞給工作進程,方法是在主進程中對它們進行pickle,並在工作進程中對它們進行unpickling。 在其他函數內定義的函數不可選,這意味着multiprocessing
將無法在工作進程中成功取消它們。 要解決此問題,請在模塊的頂層定義兩個函數,而不是嵌入dirwalker
函數。
你肯定應該繼續從X
和Y
調用Z
,而不是results
。 這樣, Z
可以在所有工作進程中同時運行,而不必在主進程中一次運行一個調用。 請記住,您的callback
函數應該盡可能快,因此您不會保留處理結果。 在那里執行Z
會減慢速度。
這里有一些簡單的示例代碼,與您正在執行的操作類似,希望能讓您了解代碼的外觀:
import multiprocessing as mp
import os
# X() reads files and grabs lines, calls helper function to calculate
# info, and returns stuff to the callback function
def X(f):
fileinfo = Z(f)
return fileinfo
# Y() reads other types of files and does the same thing
def Y(f):
fileinfo = Z(f)
return fileinfo
# helper function
def Z(arr):
return arr + "zzz"
def dirwalker(directory):
ahlala = []
# results() is the callback function
def results(r):
ahlala.append(r) # or .append, haven't yet decided
for _,_,files in os.walk(directory):
pool = mp.Pool(mp.cpu_count())
for f in files:
if len(f) > 5: # Just an arbitrary thing to split up the list with
pool.apply_async(X, args=(f,), callback=results) # ,error_callback=handle_error # In Python 3, there's an error_callback you can use to handle errors. It's not available in Python 2.7 though :(
else:
pool.apply_async(Y, args=(f,), callback=results)
pool.close()
pool.join()
return ahlala
if __name__ == "__main__":
print(dirwalker("/usr/bin"))
輸出:
['ftpzzz', 'findhyphzzz', 'gcc-nm-4.8zzz', 'google-chromezzz' ... # lots more here ]
編輯:
您可以使用multiprocessing.Manager
類創建在父進程和子進程之間共享的dict對象:
pool = mp.Pool(mp.cpu_count())
m = multiprocessing.Manager()
helper_dict = m.dict()
for f in files:
if len(f) > 5:
pool.apply_async(X, args=(f, helper_dict), callback=results)
else:
pool.apply_async(Y, args=(f, helper_dict), callback=results)
然后讓X
和Y
接受一個名為helper_dict
的第二個參數(或者你想要的任何名字),然后你就完成了。
需要注意的是,這可以通過創建包含普通dict的服務器進程來工作,並且所有其他進程通過Proxy對象與該dict進行通信。 因此,每當您閱讀或寫入字典時,您都在進行IPC。 這使它比真正的字典慢很多。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.