Manager進程之間共享數據

本文分為如下部分

  • 引言
  • 初試manager
  • 注意事項
  • 分散式進程

引言

在多進程中,每一個進程都有自己的變數拷貝,所以主進程中的一個變數傳入其他進程修改,得到的結果仍然存儲於那個進程中,主進程中這個變數其實相當於沒有被修改過。為了能讓其他進程的修改能夠同步到主進程上來,需要創建能在多個進程之間共享的變數。

舉一個例子

from multiprocessing import Processdef f1(x, l): x += 1 l.append(2)def f2(x, l): x -= 2 l.append(3)if __name__ == __main__: x = 0 l = [1] p1 = Process(target=f1, args=(x, l)) p2 = Process(target=f2, args=(x, l)) p1.start() p2.start() p1.join() p2.join() print(x, l)

運行結果為

0 [1]

x l都沒有被改變,因為它們是放在其他進程中修改的。

初試manager

我們在前面的文章中提到了進程之間共享數據的一些方法,如Queue pipe value,不過multiprocessing模塊還提供了一種更加高級的封裝,即用Manager來創建變數用於進程之間共享,我們直接來看下面一個例子

from multiprocessing import Process, Managerdef f1(ns, l): ns.x += 1 l.append(2)def f2(ns, l): ns.x -= 2 l.append(3)if __name__ == __main__: manager = Manager() ns = manager.Namespace() l = manager.list([1]) ns.x = 0 p1 = Process(target=f1, args=(ns, l)) p2 = Process(target=f2, args=(ns, l)) p1.start() p2.start() p1.join() p2.join() print(ns, l)

結果如下

Namespace(x=-1) [1, 2, 3]

上面代碼涉及到了manager.Namespace()manager.list()兩個方法,前者可以通過.來創建各種變數,後者專門用來創建list,用manager方法創建出來的變數可以在不同進程之中修改。

manager創造的其他類型詳見官網

注意事項

有時候使用manager仍會發現變數沒有被其他進程改變,比如使用manager.Namespace()創建列表修改無效,或manager.list()創建多層列表時裡面列表中的元素修改無效。這是因為它們是可變對象,修改時內存地址不變,於是主進程還是讀取原來的地址,獨到的還是原來的值。

這個回答中的代碼非常恰當,我就直接貼在這裡了

import multiprocessingimport timedef f(ns, ls, di): ns.x += 1 ns.y[0] += 1 ns_z = ns.z ns_z[0] += 1 ns.z = ns_z ls[0] += 1 ls[1][0] += 1 ls_2 = ls[2] ls_2[0] += 1 ls[2] = ls_2 di[0] += 1 di[1][0] += 1 di_2 = di[2] di_2[0] += 1 di[2] = di_2if __name__ == __main__: manager = multiprocessing.Manager() ns = manager.Namespace() ns.x = 1 ns.y = [1] ns.z = [1] ls = manager.list([1, [1], [1]]) di = manager.dict({0: 1, 1: [1], 2:[1]}) print(before, ns, ls, di) p = multiprocessing.Process(target=f, args=(ns, ls, di)) p.start() p.join() print(after, ns, ls, di)

運行結果為

before Namespace(x=1, y=[1], z=[1]) [1, [1], [1]] {0: 1, 1: [1], 2: [1]}after Namespace(x=2, y=[1], z=[2]) [2, [1], [2]] {0: 2, 1: [1], 2: [2]}

上面結果ns ls di三部分分開看,每一部分都是第一個第三個變了,第二個沒變,讀者可以細細感受它們之間的不同

分散式進程

正常分散式應該是在兩台電腦上運行的,不過我只有一台電腦,於是就打開兩個cmd,運行兩個文件,模擬分散式運行了

下面會創建master.py和task1.py兩個文件

  • master.py文件創建一個列表,這個文件負責在列表中append數據。因為只有一個進程於是沒有用Process,而是線性執行
  • task1.py文件需要對master.py文件中的列表進行pop

實現思路如下

  • master.py文件中列表正常創建並while True循環不斷添加元素
  • master.py文件要設置一個賬號密碼,將這個list變數暴露出來
  • task1.py文件要通過賬號密碼連接到master.py文件,並提取list變數

master.py文件內容如下

import random, timefrom multiprocessing.managers import BaseManager as bml = []def return_l(): return lif __name__ == __main__: # 三步固定這麼做即可 bm.register(get_l, callable = return_l) m = bm(address = (127.0.0.1, 5000), authkey = babc) m.start() new_l = m.get_l() while True: new = random.randint(0, 100) new_l.append(new) print(produce {} now all {}.format(new, new_l)) time.sleep(2 * random.random()) m.shutdown() print(master exit)

task1.py文件內容如下

import randomimport timefrom multiprocessing.managers import BaseManager as bmif __name__ == __main__: bm.register(get_l) m = bm(address = (127.0.0.1, 5000), authkey = babc) m.connect() l = m.get_l() while True: print(drop {}.format(l.pop())) time.sleep(3 * random.random())

其實看著上面代碼結合之前提供的思路應該就能知道分散式是怎麼做的了,下面講一下運行如何操作。

保存好這兩個文件後,分別在這兩個文件所在位置打開cmd,下面我就分別稱為cmdm和cmdt了

在cmdm中輸入

python master.py

這時就會發現程序開始運行,list中元素也越來越多。然後在cmdt中輸入

python task1.py

就會發現cmdm中的list元素會開始減少。

這個過程其實就是運行兩個文件,只要保證master.py運行在前即可。運行兩個文件其實相當於開啟兩個進程,在兩台電腦中開啟也是一樣。

注意:上面代碼在windows系統中運行。linux下分散式進程可以參考廖雪峰老師的這篇文章,這篇文章中的代碼在windows下是無法運行的,將其改寫成windows下的版本參考這篇文章

專欄信息

專欄主頁:python編程

專欄目錄:目錄

版本說明:軟體及包版本說明


推薦閱讀:

windows的進程和線程1--基本的概念
淺談操作系統-進程與線程

TAG:Python | Python入門 | 進程 |