Python multiprocessing模块manager封装简述

众所周知,Python是使用伪多线程运行的。这导致在多核情况下,CPU并没有被良好地利用起来。为了提高性能,我们一般会选择多进程进行工作。

进程之间的协作需要通过通信完成,考虑到管道的缓存能力和安全性,我们选择队列作为通信内容的载体。

multiprocessing模块下实现封装有很多基础类型供给多线程间共享调用,这足够满足我们的需要。

但是又一个问题出现了。进程的发送方可以根据需要向队列中加入数据,但是对于接收方不断地接收数据来保证加入队列的数据可以得到及时地处理。或许轮询式的尝试从队列中获取数据是个好选择。

那么多久循环一次呢?无限制循环?一个Linux tick时间?无论多长,在空闲的时候都会造成对CPU的性能无意义损耗。所以另一个解决方案呼之欲出,如果队列为空那么等待一个信号,直到队列中被放入数据后触发这个信号后继续该进程。这个借助multiprocessing.Event()便可轻易做到。
那么怎么对Queue进行封装呢?关于这一点参考之前写过的这篇文章: Python中轮询触发更替事件驱动的简单方法
那么现在,我们已经成功得对Queue进行了封装,那么我们是否可以对multiprocessing中manager里的其他的管理的数据内容进行封装呢?于是我给出了如下的解决方案。

import multiprocessing

#这是所有操作符相关过或者其他功能相关的特殊函数。
common_list = {"__add__","__concat__","__contains__",
               "__truediv__","__floordiv__", "__and___",
               "__xor__","__invert__","__or__","__pow__",
               "__is__","__is_not__","__setitem__","__delitem__",
               "__getitem__","__lshift__","__mod__","__mul__",
               "__matmul__","__neg__","__not__","__pos__",
               "__rshift__","__setitem__","__delitem__","__getitem__",
               "__mod__","__sub__","__truth__","__lt__",
               "__le__","__eq__","__ne__","__ge__",
               "__gt__","__str__"}
#定义需要封装事件信号的类型和触发事件信号的方法
source_map = {
    'Queue': {'put'},
    'dict':{'__setitem__'}
}

# 封装了multiprocessing.Manager,
# 由于multiprocessing.Manager实际是一个方法,
# 所以使用了下述__getattribute__的方式。
class Manager(object):
    def __init__(self):
        self.manager = multiprocessing.Manager()

    # 将multiprocessing.Manager的方法映射向了Manager
    def __getattribute__(self, name):
        # 如果该方法在source_map 内,则对该方法进行封装事件处理
        if name in source_map:
            # 初始化的方法,实例化被封装的实例和该实例所用的事件信号
            def __init__(self_q, *args, **kwargs):
                self_q.sign = self.manager.Event()
                self_q.base = self.manager.__getattribute__(name)(*args, **kwargs)

            #生成一个对应的函数的函数
            def functionfactory(i,m,self=None):
                if self:
                    def f(*args, **kwargs):
                        r = self.base.__getattribute__(m)(*args, **kwargs)
                        if i:
                            self.sign_set()
                        return r
                else:
                    def f(self_q, *args, **kwargs):
                        r = self_q.base.__getattribute__(m)(*args, **kwargs)
                        if i:
                            self_q.sign_set()
                        return r
                return f

            # 对一些属性方法做映射
            def __getattribute__(self_q, name_q):
                if name_q[:5] == 'sign_':
                    return self_q.sign.__getattribute__(name_q[5:])
                elif name_q in source_map[name] and name_q not in common_list:
                    return functionfactory(True, name_q, self_q)
                elif name_q[:2]=='__' or name_q in ['sign', 'base'] or \
                    (name_q in common_list and name_q in source_map[name]):
                    return object.__getattribute__(self_q, name_q)
                else:
                    return self_q.base.__getattribute__(name_q)

            # __dir__ 方法的重写
            def __dir__(self):
                r = (dir(self.base) 
                    + ["sign_%s" % m for m in dir(self.sign) if m[:2]!='__']
                    + ['sign', 'base'])
                r.sort()
                return r

            # 生成动态生成类型的方法字典
            method_map = {
                      '__init__': __init__, 
                      '__getattribute__': __getattribute__,
                      '__dir__': __dir__
                  }
            
            for m in common_list.intersection(
                              dir(type(self.manager.__getattribute__(name)()))
                          ):
                method_map[m] = functionfactory(m in source_map[name],m)

            #生成并返回动态类型
            return type(name, (object,), method_map)

        elif name in ['manager', 'source_map']:
            return object.__getattribute__(self, name)

        else:
            return self.manager.__getattribute__(self, name)

    def __dir__(self):
        r = dir(self.manager) + ['manager']
        r.sort()
        return r


if __name__ == "__main__":
    m = Manager()

    q = m.Queue()
    q.sign_set()
    print(q.sign_is_set())       # True
    q.sign_clear()
    q.put(1)
    print(q.sign_wait())         # True
    d = m.dict()
    d[3] = 2
    print(d.sign_is_set())       # True
    d.sign_clear()
    print(d[3])                  # 2
    print(d.sign_is_set())       # False
    print(d)                     # {3:2}

其中,通过重写__getattribute__方法的方式,简单实现了对封装的外部类和对应内部类的属性进行了映射。即当在代码中调用a.b的时候,其实是通过a.__getattribute__返回的b属性。比如:

class AllMethodAllowed(object):
    def __getattribute__(self, name):
         if name in dir(object):
             return object.__getattribute__(self, name)
         else:
             return 'Hello, %s !' % name

if __name__=="__main__":
    ama = AllMethodAllowed()
    print(ama.Emma)                # Hello, Emma !
    print(ama.World)               # Hello, World !

但是python的基本操作是由实例直接对应的内置方法决定的而非__getattribute__方法获取的,所以需要通过定义的方式进行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容