標籤:

Python多進程編程中進程池鎖共享問題

  python多進程編程使用進程池非常的方便管理進程,但是有時候子進程之間會搶佔一些獨佔資源,比如consol或者比如日誌文件的寫入許可權,這樣的時候我們一般需要共享一個Lock來對獨佔資源加鎖。lock作為一個不可直接打包的資源是沒有辦法作為一個參數直接給Pool的map方法里的函數傳參的。為了解決這個問題,有兩種解決方法,一種是使用多進程的管理器Manager(),並使用偏函數的辦法傳遞對象Manager.Lock()。第二種是在進程池創建時傳遞multiprocessing.Lock()對象。

  下面以一個具體的栗子來說明。

  比如我現在有一個數據列表我想通過多進程的方式將裡面的數據發送到指定的API並且在日誌文件中記錄每次請求所用的時間。

  我們最容易想到的解決辦法就是把鎖作為一個參數傳進去:

from multiprocessing import Pool, Lockimport urllib2from time import clockfrom functools import partialdef send_request(lock, data): api_url = "http://api.xxxx.com/?data=%s" start_time = clock() print urllib2.urlopen(api_url % data).read() end_time = clock() lock.acquire() whit open("request.log", "a+") as logs: logs.write("request %s cost: %s
" % (data, end_time - start_time)) lock.release()if __name__ == "__main__": data_list = ["data1", "data2", "data3"] pool = Pool(8) lock = Lock() partial_send_request(send_request, lock=lock) pool.map(partial_send_request, data_list) pool.close() pool.join()

  在這樣的情況下,lock作為一個不可直接打包的資源是沒有辦法作為一個參數直接給Pool的map方法里的函數傳參的。

  會出現一個運行時錯誤:

Runtime Error: Lock objects should only be shared between processes through inheritance.

  根據一開始的思路我們可以把代碼改成下面的樣子:

  第一種思路,使用Manager。

  send_request函數不用改變,只改變main中的內容:

if __name__ == "__main__": from multiprocessing import Manager data_list = ["data1", "data2", "data3"] pool = Pool(8) manager = Manager() lock = manager.Lock() partial_send_request(send_request, lock=lock) pool.map(partial_send_request, data_list) pool.close() pool.join()

  這是第一種方法,但是對於僅僅需要一個日誌寫入鎖就用一個Manager顯的十分重了。這種方式其實是需要一個專門的進程去處理Manager服務。所有的加鎖和釋放鎖的操作都是通過IPC傳遞給Manager服務的。

  第二種解決思路就是通過initializer參數在Pool對象創建時傳遞Lock對象。這種方式將Lock對象變為了所有子進程的全局對象。

  代碼可以作如下修改:

def send_request(data): api_url = "http://api.xxxx.com/?data=%s" start_time = clock() print urllib2.urlopen(api_url % data).read() end_time = clock() lock.acquire() whit open("request.log", "a+") as logs: logs.write("request %s cost: %s
" % (data, end_time - start_time)) lock.release()def init(l): global lock lock = lif __name__ == "__main__": data_list = ["data1", "data2", "data3"] lock = Lock() pool = Pool(8, initializer=init, initargs=(lock,)) pool.map(send_request, data_list) pool.close() pool.join()

  這樣的修改就沒有使用偏函數的必要了。

原文在我的blog上:歡迎關注聽雨軒

推薦閱讀:

TAG:Python | 编程 |