Pool類
在使用Python進行系統管理時,特別是同時操作多個文件目錄或者遠程控制多台主機,並行操作可以節約大量的時間。如果操作的對象數目不大時,還可以直接使用Process類動態的生成多個進程,十幾個還好,但是如果上百個甚至更多,那手動去限制進程數量就顯得特別的繁瑣,此時進程池就派上用場了。
Pool類可以提供指定數量的進程供用戶調用,當有新的請求提交到Pool中時,如果池還沒有滿,就會創建一個新的進程來執行請求。如果池滿,請求就會告知先等待,直到池中有進程結束,才會創建新的進程來執行這些請求。
下面介紹一下multiprocessing 模塊下的Pool類下的幾個方法
apply()
函數原型:
apply(func[, args=()[, kwds={}]])
該函數用於傳遞不定參數,主進程會被阻塞直到函數執行結束(不建議使用,並且3.x以后不在出現)。
apply_async()
函數原型:
apply_async(func[, args=()[, kwds={}[, callback=None]]])
與apply用法一樣,但它是非阻塞且支持結果返回進行回調。
map()
函數原型:
map(func, iterable[, chunksize=None])
Pool類中的map方法,與內置的map函數用法行為基本一致,它會使進程阻塞直到返回結果。
注意,雖然第二個參數是一個迭代器,但在實際使用中,必須在整個隊列都就緒后,程序才會運行子進程。
close()
關閉進程池(pool),使其不在接受新的任務。
terminate()
結束工作進程,不在處理未處理的任務。
join()
主進程阻塞等待子進程的退出,join方法必須在close或terminate之后使用。
multiprocessing.Pool類的實例:
執行結果:
上例是一個創建多個進程並發處理與順序執行處理同一數據,所用時間的差別。從結果可以看出,並發執行的時間明顯比順序執行要快很多,但是進程是要耗資源的,所以平時工作中,進程數也不能開太大。
程序中的r1表示全部進程執行結束后全局的返回結果集,run函數有返回值,所以一個進程對應一個返回結果,這個結果存在一個列表中,也就是一個結果堆中,實際上是用了隊列的原理,等待所有進程都執行完畢,就返回這個列表(列表的順序不定)。
對Pool對象調用join()方法會等待所有子進程執行完畢,調用join()之前必須先調用close(),讓其不再接受新的Process了。
再看一個實例:
執行結果:
再次執行結果如下:
結果中為什么還有空行和沒有折行的數據呢?其實這跟進程調度有關,當有多個進程並行執行時,每個進程得到的時間片時間不一樣,哪個進程接受哪個請求以及執行完成時間都是不定的,所以會出現輸出亂序的情況。那為什么又會有沒這行和空行的情況呢?因為有可能在執行第一個進程時,剛要打印換行符時,切換到另一個進程,這樣就極有可能兩個數字打印到同一行,並且再次切換回第一個進程時會打印一個換行符,所以就會出現空行的情況。
在利用Python進行系統管理的時候,特別是同時操作多個文件目錄,或者遠程控制多台主機,並行操作可以節約大量的時間。當被操作對象數目不大時,可以直接利用multiprocessing中的Process動態成生多個進程,10幾個還好,但如果是上百個,上千個目標,手動的去限制進程數量卻又太過繁瑣,這時候進程池Pool發揮作用的時候就到了。
Pool可以提供指定數量的進程,供用戶調用,當有新的請求提交到pool中時,如果池還沒有滿,那么就會創建一個新的進程用來執行該請求;但如果池中的進程數已經達到規定最大值,那么該請求就會等待,直到池中有進程結束,才會創建新的進程來它。這里有一個簡單的例子:
先創建容量為3的進程池,然后將f(i)依次傳遞給它,運行腳本后利用ps aux | grep pool.py查看進程情況,會發現最多只會有三個進程執行。pool.apply_async()用來向進程池提交目標請求,pool.join()是用來等待進程池中的worker進程執行完畢,防止主進程在worker進程結束前結束。但必pool.join()必須使用在pool.close()或者pool.terminate()之后。其中close()跟terminate()的區別在於close()會等待池中的worker進程執行結束再關閉pool,而terminate()則是直接關閉。result.successful()表示整個調用執行的狀態,如果還有worker沒有執行完,則會拋出AssertionError異常。
利用multiprocessing下的Pool可以很方便的同時自動處理幾百或者上千個並行操作,腳本的復雜性也大大降低。