上一篇文章: 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帅哥