开发并行程序的时候,会碰到在不同进程访问同一个对象的情况,python下面有个multiprocessing.Manager,使用这个类,可以创建共享对象(shared object),其他进程就可以通过proxy访问这个这些共享对象了。

举个例子,定义一个共享的list对象,有两个写进程入,他们每秒在这个list写入”PID:进程ID, NUM:随机数”这样的内容,一个读取进程每2秒读取这个list的内容:

import multiprocessing
import random
import time
import os

# 写入进程执行的内容
def writer(my_list):
    pid = os.getpid()
    while True:
        time.sleep(1)
        my_list.append('PID:{}, NUM:{}'.format(pid, random.randint(0, 100)))

# 读取进程执行的内容
def reader(my_list):
    while True:
        time.sleep(2)
        if len(my_list) > 0:
            print('-----------------------------')
            print('\n'.join(my_list))


if __name__ == '__main__':
    with multiprocessing.Manager() as manager: # 创建manager对象
        my_list = manager.list() # 共享对象my_list
        wp1 = multiprocessing.Process(target=writer, args=(my_list, ))
        wp2 = multiprocessing.Process(target=writer, args=(my_list, ))
        wp1.start()
        wp2.start()

        rp = multiprocessing.Process(target=reader, args=(my_list, ))
        rp.start()

        wp1.join()
        wp2.join()
        rp.join()

代码比较简单,不废话了。

Manager定义在multiprocessing.managers.py文件下面,从文件里看,Manger()指向的其实是SyncManager

SyncManager继承自BaseManager,后面定义自己的manager还会用到BaseManager这个类。

SyncManager的内容:

class SyncManager(BaseManager):
    '''
    Subclass of `BaseManager` which supports a number of shared object types.

    The types registered are those intended for the synchronization
    of threads, plus `dict`, `list` and `Namespace`.

    The `multiprocessing.Manager()` function creates started instances of
    this class.
    '''

不是我懒,是它确实什么都没有~真的就只继承了BaseManager,SyncManager主要干的活是调用register来注册一些可以通过proxy访问的对象,比如上面我们用到的list对象,managers.py文件里是这么注册list对象的:

SyncManager.register('list', list, ListProxy)

register是BaseManager的类方法,长这样:

@classmethod
def register(cls, typeid, callable=None, proxytype=None, exposed=None,
             method_to_typeid=None, create_method=True):
    '''
    Register a typeid with the manager type
    '''
    if '_registry' not in cls.__dict__:
        cls._registry = cls._registry.copy()

    if proxytype is None:
        proxytype = AutoProxy

BaseManager类的_registry里面保存了所有注册过的类型,看下面:

如果proxytype是空的话,会调用AutoProxy。

上面扯了这么多,如果我们想共享一个自己的定义的类,怎么做?仿照SyncManger的写法就可以了,官方例子是这样的:

from multiprocessing.managers import BaseManager

class MathsClass:
    def add(self, x, y):
        return x + y
    def mul(self, x, y):
        return x * y

class MyManager(BaseManager):
    pass

MyManager.register('Maths', MathsClass)

if __name__ == '__main__':
    with MyManager() as manager:
        maths = manager.Maths()
        print(maths.add(4, 3))         # prints 7
        print(maths.mul(7, 8))         # prints 56

应该还是蛮简单的,如果想在自己的manager类里面注册list,可以仿照SyncManager的写法,MyManager.register(‘list’, list, ListProxy)。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注