2017-06-14 58 views
0

我一直在使用multiprocessing軟件包來加速某些地理處理(GIS/arcpy)任務,這些任務是多餘的,需要對2000多個相似的幾何體進行相同的處理。爲什麼我們必須顯式傳遞常量到多處理函數中?

分離效果很好,但我的「工作人員」功能相當長且複雜,因爲從頭到尾的任務本身都很複雜。我很想分開更多的步驟,但是我無法將信息傳遞給/從worker函數,因爲出於某種原因,需要明確傳入多處理使用的worker函數。

這意味着我無法在if __name__ == '__main__'正文中定義常量,然後在輔助函數中使用它們。這也意味着我的工作函數參數列表變得非常長 - 這是超級醜陋,因爲嘗試使用多個參數也需要創建一個幫助器「星號」函數,然後itertools將它們重新解壓縮(第二秒回答於this question)。

我在下面創建了一個簡單的例子,演示了我在說什麼。是否有任何解決方法 - 我應該使用的不同方法 - 或者至少可以解釋一下爲什麼這是它的方式?

注意:我在Windows Server 2008 R2 Enterprise x64上運行此操作。

編輯:我似乎沒有把我的問題弄清楚。我並不關心pool.map如何只接受一個參數(儘管它很煩人),而是我不明白爲什麼在if __name__ == '__main__'以外定義的函數的作用域如果用作多處理函數時不能訪問該塊內定義的東西 - 除非你明確地將它作爲參數傳遞,這是令人討厭的。

import os 
import multiprocessing 
import itertools 

def loop_function(word): 
    file_name = os.path.join(root_dir, word + '.txt') 
    with open(file_name, "w") as text_file: 
     text_file.write(word + " food") 

def nonloop_function(word, root_dir): # <------ PROBLEM 
    file_name = os.path.join(root_dir, word + '.txt') 
    with open(file_name, "w") as text_file: 
     text_file.write(word + " food") 

def nonloop_star(arg_package): 
    return nonloop_function(*arg_package) 

# Serial version 
# 
# if __name__ == '__main__': 
# root_dir = 'C:\\hbrowning' 
# word_list = ['dog', 'cat', 'llama', 'yeti', 'parakeet', 'dolphin'] 
# for word in word_list: 
#  loop_function(word) 
# 
## -------------------------------------------- 

# Multiprocessing version 
if __name__ == '__main__': 
    root_dir = 'C:\\hbrowning' 
    word_list = ['dog', 'cat', 'llama', 'yeti', 'parakeet', 'dolphin'] 
    NUM_CORES = 2 
    pool = multiprocessing.Pool(NUM_CORES, maxtasksperchild=1) 

    results = pool.map(nonloop_star, itertools.izip(word_list, itertools.repeat(root_dir)), 
        chunksize=1) 
    pool.close() 
    pool.join() 
+0

我不明白你爲什麼需要在'__name__'塊中定義它們,而不是在模塊級別,這將起作用。 –

+0

實際上,在塊中定義事物對我來說也很好。你爲什麼認爲你需要將所有東西都壓縮起來? –

+0

因爲Python 2.7的'pool.map'只接受一個函數和一個參數(再次,從我鏈接的問題)。我很困惑爲什麼事情正在爲你工作 - 也許你可以發佈一些有效的代碼? – HFBrowning

回答

2

的問題是,至少在Windows上(雖然有與* nix的類似警告多的fork風格,太),當您執行腳本,它(極大地簡化IT)有效地結束了作爲,如果你叫兩個空白(殼)與subprocess.Popen()處理,然後讓他們執行:只要那些公關的一個

由一個
python -c "from your_script import nonloop_star; nonloop_star(('dog', 'C:\\hbrowning'))" 
python -c "from your_script import nonloop_star; nonloop_star(('cat', 'C:\\hbrowning'))" 
python -c "from your_script import nonloop_star; nonloop_star(('yeti', 'C:\\hbrowning'))" 
python -c "from your_script import nonloop_star; nonloop_star(('parakeet', 'C:\\hbrowning'))" 
python -c "from your_script import nonloop_star; nonloop_star(('dolphin', 'C:\\hbrowning'))" 

一個過去的通話結束。這意味着您的if __name__ == "__main__"塊永遠不會被執行(因爲它不是主腳本,所以它被作爲模塊導入),因此在其中聲明的任何內容都不可用於該函數(因爲它從未被評估過)。

對於你的函數之外的工作人員,你至少可以通過訪問與globals()module通過sys.modules["your_script"]甚至欺騙但這僅適用於被評估的工作人員,所以任何置於if __name__ == "__main__"後衛內不可用,因爲它沒」甚至有機會。這也是爲什麼你必須在Windows上使用這個守護進程的原因 - 沒有它你會執行你的池創建,以及你嵌入到守衛中的其他代碼,一遍又一遍地處理每個產生的進程。

如果您需要在多重處理功能中共享只讀數據,只需在腳本的全局名稱空間中定義它,而不是在__main__警衛之外,並且所有功能都有權訪問它在開始新流程時進行評估),而不管它們是否作爲單獨的流程運行。

如果你需要改變的數據,那麼你需要使用一些可以在不同進程間自我同步的東西 - 爲此設計了許多模塊,但大部分時間是Python自己的基於pickle的數據報文通信multiprocessing.Manager(和它提供的類型),雖然速度慢,但不夠靈活,就足夠了。

+0

這是一個很棒的答案@zwer!我沒有解釋,但是我不想在模塊級環境中包含數據的理由是計算起來非常昂貴(3小時),而且我只需要在每次腳本運行時執行一次。然後我以平行的方式製作了2000份報告,併發布了相同的數據。這聽起來像將它們分成兩個腳本,並且將數據路徑用作後者中的常量將是更清晰的方法。但至少我現在理解了程序執行流程 - 這是我根據文檔無法理解的。 – HFBrowning

+1

@HFBrowning - 如果需要很長時間來評估您的數據(假設數量很大)並且您的流程需要無限制地訪問整個數據集 - 我建議您將它存儲在某些內存數據庫中(['Redis'] (例如https://redis.io/)),然後讓你的進程輪詢來自它的數據。如果進程可以以序列化方式使用數據(因此不需要一次設置整個集合),則可以創建一個流式調度程序模式,如[this](https://stackoverflow.com/a/44502827/7553525)或[this](https://stackoverflow.com/a/44415368/7553525),並以受控方式餵養你的員工。 – zwer

+0

我從來沒有聽說過'Redis' - 它看起來棒極了!感謝您提供更多建議(: – HFBrowning

0

Python » 3.6.1 Documentation: multiprocessing.pool.Pool

map(func, iterable[, chunksize]) 
A parallel equivalent of the map() built-in function (it supports only one iterable argument though) 

沒有限制,只是它必須是一個迭代!
嘗試class Container,例如:

class WP(object): 
    def __init__(self, name): 
     self.root_dir ='C:\\hbrowning' 
     self.name = name 

word_list = [WP('dog'), WP('cat'), WP('llama'), WP('yeti'), WP('parakeet'), WP('dolphin')] 
results = pool.map(nonloop_star, word_list, chunksize=1) 

Note: The Var Types inside the class have to be pickleable !
Read about what-can-be-pickled-and-unpickled

相關問題