python多進程編程使用進程池非常的方便管理進程,但是有時候子進程之間會搶占一些獨占資源,比如consol或者比如日志文件的寫入權限,這樣的時候我們一般需要共享一個Lock來對獨占資源加鎖。
lock作為一個不可直接打包的資源是沒有辦法作為一個參數直接給Pool的map方法裡的函數傳參的。
為了解決這個問題,有兩種解決方法,一種是使用多進程的管理器Manager(),並使用偏函數的辦法傳遞對象Manager.Lock()。第二種是在進程池創建時傳遞multiprocessing.Lock()對象。
示例:
比如我現在有一個數據列表我想通過多進程的方式將裡面的數據發送到指定的API並且在日志文件中記錄每次請求所用的時間。
我們最容易想到的解決辦法就是把鎖作為一個參數傳進去:
from multiprocessing import Pool, Lock
import urllib2
from time import clock
from functools import partial
def 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\n' % (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中的內容:
''' 學習中遇到問題沒人解答?小編創建了一個Python學習交流QQ群:153708845 尋找有志同道合的小伙伴,互幫互助,群裡還有不錯的視頻學習教程和PDF電子書! '''
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\n' % (data, end_time - start_time))
lock.release()
def init(l):
global lock
lock = l
if __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()
這樣的修改就沒有使用偏函數的必要了。