上一篇文章: Python進程專題6:共享數據與同步
下一篇文章: Python進程專題8:分佈集羣的消息傳遞
進程不支持共享對象,上面描述的建立共享值和數組,但都是指定的特殊類型,對高級的Python對象(如:字典、列表、用戶自定義類的實例)不起做用。還好multiprocessing模塊提供了一種使用共享對象的途徑:單前提是這些對象運行在所謂的【管理器】的控制之下。segmentfault
管理器是獨立的子進程,其中存在真實的對象,並以服務器的形式運行,其餘進程經過使用代理訪問共享對象,這些數組
代理做爲管理器服務器的客戶端而運行。服務器
m=Manager():在一個獨立的進程中建立運行的管理器,返回值是SyncManager(定義在multiprocess.managersa模塊中)類的實例。 m能夠建立共享對象
在服務器上建立共享的Array實例並返回能夠訪問它的代理。
在服務器上建立共享的threading.BoundedSemphore實例,並返回可訪問它的代理。
在服務器上建立共享的threading.Condition實例,並返回可訪問它的代理。 lock是m.Lock()或m.Rlock()方法建立的代理實例
在服務器上建立共享的dict(字典)實例,並返回能夠訪問它的代理。
在服務器上建立共享的thread.Event實例,並返回能夠訪問它的代理。
在服務器上建立共享的list實例,並返回能夠訪問它的代理。
子服務器上建立共享的threading.Lock實例,並返回可訪問它的代理。
在服務器建立共享的namespace對象,並返回能夠訪問它的代理。 namespace就是一個相似於Python模塊的對象。例如:若是n是一個命名空間代理,那麼可使用(.)賦值和讀取屬性, 例如:n.name=value 或value=n.name。但name的名稱十分重要,若是name以字母開頭,那麼這個值就是管理器所擁有的共享對象的一部分, 全部其它進程都可訪問它,若是name如下劃線開頭,那麼它只是代理對象的一部分,並非共享。
在服務器上建立共享的Queue.Queue對象,並返回可訪問它的代理。
在服務器上建立共享的threading.Rlick對象,並返回可訪問它的代理。
在服務器上建立共享的threading.Semaphore對象,並返回可訪問它的代理。
在服務器上建立一個共享的Value對象,並返回可訪問它的代理。
#使用管理器建立一個在進程間共享的字典 import multiprocessing import time def watch(dict,event): while True: event.wait() print(dict) event.clear() if __name__=="__main__": #實例化一個管理器 m=multiprocessing.Manager() #在服務器上建立一個共享的字典,並返回能夠調用它的代理 dict=m.dict() #在服務器上建立一個共享的Event event=m.Event() #啓動監視字典的進程 p=multiprocessing.Process(target=watch,args=(dict,event,)) #設置爲後臺進程,隨主進程的銷燬而銷燬,該後臺進程沒法建立新的線程 p.daemon=True p.start() #更新字典並通知監聽者 dict["name"]="mark" event.set() time.sleep(3) # 更新字典並通知監聽者 dict["age"] = 18 event.set() time.sleep(3) #終止進程和管理器 p.terminate() m.shutdown()
運行結果:網絡
若是想要共享用戶自定義類的實例,則必須建立自定義管理器對象,爲此,須要建立一個繼承自BaseManager類的類,其中BaseManager類定義在multiprocessing.managers子模塊中。函數
managers.BaseManager(address,authkey):用戶自定對象建立管理器服務器的基類。 address是一個可選的元組(hostname,port),用於指定服務器的網絡地址。若是忽略此參數,操做系統將簡單 的分配一個對應於某些空閒端口號的地址。 authkey是一個字符串,用於對鏈接到服務器的客戶端身份進行驗證。若是忽略此參數,將是喲個current_process().authkey的值。
假設myclass是一個繼承自BaseManager的類,可使用如下類方法來建立用於返回代理給共享對象的方法。spa
myclass.register(typeid,callable,proxytype,exposed,method_to_typeid,create_method):使用管理器類註冊一種新的數據類型。 typeid:一個字符串,用於命名特殊類型的共享對象。這個字符串應該是有效的Python標識符。 callable:建立或返回要共享的實例的可調用對象。 proxytype:一個類,負責提供客戶端中藥使用的代理對象的實現,一般這些類是默認生成的,所以通常設置爲None。 exposed:共享對象上方法名的序列,它將會公開給代理對象,若是省略此參數,將使用proxytype_exposed的值, 若是proxytype_expose未定義,序列將包含全部的公共方法(全部不如下劃線_開頭的可調用方法)。 method_to_typeid:從方法名稱到類型IDS的映射,這裏的IDS用於指定哪些方法應該使用代理對象返回他們的結果, 若是某個方法在這個映射中找不到,將複製和返回它的返回值,若是method_to_typeid爲None,將使用proxytype_method_to_typeid_的值。 create_method:一個布爾標誌,用於指定是否在mgclass中建立名爲typeid的方法,默認值爲True。
從BaseManager類派生的管理器的實例m必須手動啓動才能運行。操作系統
屬性或者方法 | 解釋說明 |
---|---|
m.address | 元組(hostname,port),表明管理器服務器正在使用的地址。 |
m.connect() | 鏈接帶遠程管理器對象,該對象的地址在BaseManager構造函數中支出。 |
m.serve_forever() | 在當前進程中運行管理器服務器。 |
m.shutdown() | 關閉由start()啓動的管理器服務器。 |
m.start() | 啓動一個單的子進程,並在該子進程中啓動管理器服務器。 |
繼承自BaseProxy的類的實例具備如下方法:線程
proxy._callmethod(name,args,kwargs):調用代理引用對象上的方法name。 proxy._getvalue():返回調用者中引用對象的副本,若是此次調用是在另外一個進程中, 那麼引用對象將被序列化,發送給調用者,而後再進行反序列化。若是沒法序列號對象將引起異常。
代碼:代理
#繼承BaseManager類,實現自定義對象的共享,利用代理公開屬性與方法 import multiprocessing from multiprocessing.managers import BaseManager from multiprocessing.managers import BaseProxy #普通須要共享的類,代理沒法經過(.)這種形式直接訪問屬性 class MyClass(): def __init__(self,value): self.name=value def __repr__(self): return "MyClass(%s)"%self.name def getName(self): return self.name def setName(self,value): self.name=value def __add__(self,valuye): self.name+=valuye return self #經過自定義繼承BaseProxy來實現代理,從而正確的公開__add__方法,並使用特性(property)公開name屬性。 #BaseProxy來來自multiprocessing.managers模塊 class MyClassProxy(BaseProxy): #referent上公開的全部方法列表 _exxposed_=['__add__','getName','setName'] #實現代理的公共結接口 def __add__(self, value): self._callmethod('__add__',(value,)) return self @property def name(self): return self._callmethod('getName',()) @name.setter def name(self,value): self._callmethod('setName',(value,)) def __repr__(self): return "MyClass(%s)"%self.name def getName(self): return self.name def setName(self,value): self.name=value def __add__(self,valuye): self.name+=valuye return self class MyManager(BaseManager): pass #不使用代理 #MyManager.register("MyClass",MyClass) #使用代理 MyManager.register("MyClass",MyClass,proxytype=MyClassProxy) if __name__=="__main__": m=MyManager() m.start() #建立託管對象,此時知識建立了一個實例代理,沒法直接訪問屬性,必須使用函數來訪問 #代理沒法訪問特殊方法和下劃線(_)開頭的全部方法。 a=m.MyClass("mark") print(a) print(a.getName()) #不使用代理,下面兩條語句會異常 a.__add__("帥哥") print(a.name) #print(a.name) #a.__add__("帥哥")
結果:code
MyClass(mark) mark mark帥哥