Python進程專題7:託管對象

上一篇文章: Python進程專題6:共享數據與同步
下一篇文章: Python進程專題8:分佈集羣的消息傳遞
進程不支持共享對象,上面描述的建立共享值和數組,但都是指定的特殊類型,對高級的Python對象(如:字典、列表、用戶自定義類的實例)不起做用。

還好multiprocessing模塊提供了一種使用共享對象的途徑:單前提是這些對象運行在所謂的【管理器】的控制之下。segmentfault

管理器是獨立的子進程,其中存在真實的對象,並以服務器的形式運行,其餘進程經過使用代理訪問共享對象,這些數組

代理做爲管理器服務器的客戶端而運行。服務器

語法:

m=Manager():在一個獨立的進程中建立運行的管理器,返回值是SyncManager(定義在multiprocess.managersa模塊中)類的實例。
m能夠建立共享對象

經常使用方法

  • m.Array(typecode,sequence)
在服務器上建立共享的Array實例並返回能夠訪問它的代理。
  • m.BoundedSemaphore(value)
在服務器上建立共享的threading.BoundedSemphore實例,並返回可訪問它的代理。
  • m.Condition(lock)
在服務器上建立共享的threading.Condition實例,並返回可訪問它的代理。
lock是m.Lock()或m.Rlock()方法建立的代理實例
  • m.dict(args)
在服務器上建立共享的dict(字典)實例,並返回能夠訪問它的代理。
  • m.Event()
在服務器上建立共享的thread.Event實例,並返回能夠訪問它的代理。
  • m.list(sequence)
在服務器上建立共享的list實例,並返回能夠訪問它的代理。
  • m.Lock
子服務器上建立共享的threading.Lock實例,並返回可訪問它的代理。
  • m.Namespace()
在服務器建立共享的namespace對象,並返回能夠訪問它的代理。
namespace就是一個相似於Python模塊的對象。例如:若是n是一個命名空間代理,那麼可使用(.)賦值和讀取屬性,
例如:n.name=value 或value=n.name。但name的名稱十分重要,若是name以字母開頭,那麼這個值就是管理器所擁有的共享對象的一部分,
全部其它進程都可訪問它,若是name如下劃線開頭,那麼它只是代理對象的一部分,並非共享。
  • m.Queue()
在服務器上建立共享的Queue.Queue對象,並返回可訪問它的代理。
  • m.RLock()
在服務器上建立共享的threading.Rlick對象,並返回可訪問它的代理。
  • m.Semaphore(value)
在服務器上建立共享的threading.Semaphore對象,並返回可訪問它的代理。
  • m.value(typecode,value)
在服務器上建立一個共享的Value對象,並返回可訪問它的代理。
  • 實例1使用管理者建立一個在進程中共享的字典:
代碼:
#使用管理器建立一個在進程間共享的字典

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類的類,

其中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帥哥
相關文章
相關標籤/搜索