• Python并发编程之托管对象


    一、什么是托管对象

    和线程不同,进程不支持托管对象。尽管可以像前面所述那样可以创建共享值和数组,但这对更高级的python对象(如字典、列表、用户自定义对象等)而言不起作用。但是multiprocessing模块确实提供了一种使用共享对象的途径,但前提是它们运行在所谓的管理器的控制之下、管理器是独立的子进程,其中存在真实的对象,并以服务器的形式运行。其他进程通过使用代理访问共享对象,这些代理作为服务器的客户端运行。

    使用简单托管对象的最直观方式是使用Manager()函数。

    Manager()   
    
    • 1

    在一个单独的进程中创建运行的管理器。返回值是SyncMsanager类型的实例,SyncManager类型定义在multiprocessing.managers模块中。

    Manager()函数返回的SyncManager的实例m具有一系列方法,可用于创建共享对象并返回用于访问这些共享对象的代理。通常,可以创建一个管理器,并在启动任何新进程之前使用这些方法创建共享对象。

    • m.Array(typecode, sequence)
    • m.BoundedSemaphore([value])
    • m.Condition([lock])
    • m.dict([args])
    • m.Event()
    • m.list([sequence])
    • m.Lock()
    • m.Namespace()
    • m.Queue()
    • m.RLock()
    • m.Semaphore()
    • m.Value(typecode, value)

    二、托管对象示例代码

    下面的例子说明了如何使用管理器创建一个在进程之间共享的字典

    import multiprocessing
    import time
    
    # 只要设定要传递的事件,就打印d
    def watch(d, evt):
        while True:
            evt.wait()
            print(d)
            evt.clear()
            
    if __name__ == '__main__':
        m = multiprocessing.Manager()
        d = m.dict()    # 创建一个shared dict
        evt = m.Event() # 创建一个shared event
        
        # 启动监视字典的程序
        p = multiprocessing.Process(target=watch, args=(d, evt))
        p.daemon = True
        p.start()
        
        # 更新字典并通知监视者
        d['foo'] = 42
        time.sleep(5)
        evt.set()
        time.sleep(5)
        
        # 终止进程和管理器
        p.terminate()
        m.shutdown()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    三、自定义共享对象

    如果需要其他类型的共享对象,比如用户自定义类的实例,则必须创建自定义管理对象。为此,需要创建一个继承自BaseManager类的类,BaseManager类定义在multiprocessing.managers子模块中。

    managers.BaseManager([address [, authkey]])
    
    • 1

    为用户定义对象创建管理器服务器的基类。address是一个可选元组(hostname, port), 用于指定服务器的网络地址。如果省略次参数,操作系统将简单分配一个对应于某些空闲端口号的地址。authkey是一个字符串,用于对连接到服务器的客户端进行身份验证。如果省略此参数,将使用current_process().authkey的值。

    如果mgrclass是一个继承自BaseManager的类,可以使用以下类方法来创建返回代理给共享对象的方法。

    mgrclass.register(typeid [, callable [, proxytype [, exposed [, method_to_typeid [, create_method]]]]])
    
    • 1

    使用管理器类注册一种新的数据类型。

    • typeid是一个字符串,用于命名特定类型的共享对象。这个字符串应该是有效的python标识符。
    • callable是创建或返回要共享的实例的可调用对象。
    • proxytype是一个类,负责提供客户端中要使用的代理对象的实现。通常,这些类是默认生成的,因此一般置为none
    • exposed是共享对象上方法名称的序列,它将会公开给代理对象。如果省略此参数,将使用proxytype._exposed_的值,如果proxytype._exposed_未定义,序列将包含所有的公共方法。
    • method_to_typeid 是从方法名称到类型IDS的映射。如果它为none,将使用proxytype._method_to_typeid_的值。
    • create_method是一个布尔标志,用于指定是否在mgrclass中创建名为typeid的方法。默认值是true

    从BaseManager类派生的管理器的实例m必须手动启动才能运行。相关属性和方法如下:

    • m.address
    • m.connect()
    • m.serve_forever()
    • m.shutdown()
    • m.start()

    四、自定义托管对象示例代码

    下面的例子说明了如何为用户自定义类型创建管理器, 在代理上是无法访问特殊方法和以下划线开头的所有方法的,在更高级的应用程序中,可以自定义代理,从而进行更仔细的控制访问。这是通过定义继承自BaseProxy的类来实现的, 以下代码说明如何为A类自定义代理,从而正确地公开__iadd__方法,并使用属性(property)公开x特性(attribute):

    import multiprocessing
    from multiprocessing.managers import BaseManager
    from multiprocessing.managers import BaseProxy
    
    class A(object):
        def __init__(self, value) -> None:
            self.x = value
        
        def __repr__(self):
            return "A(%s)" % self.x
        
        def getX(self):
            return self.x
        
        def setX(self, value):
            self.x = value
            
        def __iadd__(self, value):
            self.x += value
            return self
    
    class AProxy(BaseProxy):
        # referent上公开的所有方法列表
        _exposed_ = ['__iadd__', 'getX', 'setX']
        # 实现代理的公共接口
        def __iadd_proxy__(self, value):
            self._callmethod('__iadd__', (value, ))
            return self
        
        @property
        def x(self):
            return self._callmethod('getX', ())
        
        @x.setter
        def x(self, value):
            self._callmethod('setX', (value, ))
    
    class MyManager(BaseManager):pass
    MyManager.register("A", A, proxytype=AProxy)
    
    if __name__ == '__main__':
        m = MyManager()
        m.start()
        a = m.A(11)
        print(a.x)
        a.x = 22
        print(a.x)
        a.__iadd_proxy__(33)
        print(a.x)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
  • 相关阅读:
    揭秘Redis的高效失效策略,提升可用性
    vue navbar tabbar导航条根据位置移动实现定位、颜色过渡动画效果
    OpenCV图像金字塔
    HTML期末学生大作业-拯救宠物网页作业html+css
    Android ContentProvider
    C#与物联网:打造智能家居解决方案
    linuxnfs服务安装与配置实践
    zookeeper集群搭建步骤
    嵌入式经典面试题
    微信小程序登录Test(仅供参考)
  • 原文地址:https://blog.csdn.net/hubing_hust/article/details/127955420