Python multiprocessingモジュールmanagerパッケージの概要

5966 ワード

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__メソッドは取得されるので、定義された方法で行う必要があります.