PythonのThreadLocal変数を深く理解する(下)

6637 ワード

前編ではThreadLocal変数の簡単な使用を見て、中編ではpythonでのThreadLocalの実現を分析したが、物語はまだ終わっていない.この記事では、WerkzeugのThreadLocalのデザインを見てみましょう.
WerkzeugはWSGIツールライブラリとして、pythonに内蔵されたThreadLocalクラスを直接使用するのではなく、独自に一連のLocalクラスを実現しています.簡単なLocalと、それに基づいて実現されるLocalStack、LocalManager、LocalProxyが含まれています.次に、これらのクラスの使用方法、設計の初心、具体的な実現テクニックを見てみましょう.
Localクラスのデザイン
Werkzeugの設計者はpythonが持っているThreadLocalが需要を満たすことができないと考えています.主に次の2つの理由からです.
  • Werkzeugは主に「ThreadLocal」を用いて同時の要求を満たし、pythonが持参したThreadLocalはスレッドベースの同時しか実現できない.pythonには他にも多くの同時方式があります.例えば、一般的なコヒーレント(greenlet)です.そのため、コヒーレントをサポートできるLocalオブジェクトを実現する必要があります.
  • WSDiは、要求を処理するために毎回新しいスレッドが生成されることを保証しません.つまり、スレッドは多重化可能です(要求を処理するためにスレッドプールを維持できます).これにより、werkzeugがpythonが持参したThreadLocalを使用すると、新しいリクエストを処理するために「不潔(以前に処理されたリクエストに関連するデータが保存されている)」スレッドが使用されます.

  • この2つの問題を解決するためにwerkzeugでLocalクラスを実現した.Localオブジェクトは、スレッドとコモン間のデータの分離を行うことができます.また、あるスレッドまたはコモンのデータのクリーンアップもサポートされます(これにより、1つのリクエストを処理した後、対応するデータをクリーンアップし、次のリクエストの到来を待つことができます).
    具体的にどのように実現されているのか、考えは非常に簡単で、PythonのThreadLocal変数(上)を深く理解する最後に、グローバル辞書を作成し、スレッド(またはスレッド)識別子をkeyとし、対応するスレッド(またはスレッド)のローカルデータをvalueとしたことを述べた.ここでwerkzeugは上記の考え方で実現されていますが、pythonの黒い魔法を利用して、最後にユーザーに明確で簡単なインタフェースを提供します.
    具体的な実装
    Localクラスの実装はwerkzeug.localでは、8 a 84 b 62バージョンのコードで分析します.前2編のThreadLocalの理解を通して、Localオブジェクトの特徴と使用方法を知っています.ここではLocalオブジェクトの使用例を示しません.コードを直接見てみましょう.
    class Local(object):
        __slots__ = ('__storage__', '__ident_func__')
        
        def __init__(self):
            object.__setattr__(self, '__storage__', {})
            object.__setattr__(self, '__ident_func__', get_ident)
        ...

    大量のLocalオブジェクトが存在する可能性があるため、Localオブジェクトが占有するスペースを節約するために、ここでは__slots__を使用してLocalが持つことができる属性を書き殺しました.
  • __storage__: 値は辞書で、実際のデータを保存し、空に初期化します.
  • __ident_func__:値は、現在のスレッドまたはスレッドのフラグを見つける関数です.

  • Localオブジェクトの実際のデータは__に保存されているためstorage__では、Localプロパティの操作は実際にはstorage__に表示されます.属性を取得するには、ここではマジックメソッド__getattr__でブロックする_storage__ および_ident_func__以外の属性を取得し、それをガイド_storage__格納されている現在のスレッドまたはコモンシップのデータ.属性値のsetまたはdelについては、それぞれ_setattr__および_setattr__(これらのマジックメソッドの紹介は属性制御を参照).キーコードは次のとおりです.
    def __getattr__(self, name):
        try:
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)
    
    def __setattr__(self, name, value):
        ident = self.__ident_func__()
        storage = self.__storage__
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}
    
    def __delattr__(self, name):
        try:
            del self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

    IDが1,2,...,NのN個のスレッドあるいはコパス、それぞれはすべてLocalオブジェクトで自分のいくつかの局部のデータを保存して、それではLocalオブジェクトの内容は下図のようです:
    さらに、Localクラスは、現在のスレッドまたはコパスで保存されているデータを解放するための__release_local__メソッドを提供します.
    Local拡張インタフェース
    WerkzeugはLocalに基づいてLocalStackとLocalManagerを実現し、より友好的なインタフェースサポートを提供しています.
    LocalStack
    LocalStackはLocalをカプセル化することによってスレッド(またはコモン)の独立したスタック構造を実現し、注釈には具体的な使用方法があり、簡単な使用例は以下の通りである.
    ls = LocalStack()
    ls.push(12)
    print ls.top    # 12
    print ls._local.__storage__
    # {140735190843392: {'stack': [12]}}

    LocalStackの実装は興味深いが,Localオブジェクトを独自の属性_localとし,インタフェースpush,pop,topメソッドを定義して対応するスタック動作を行う.ここでは_local.__storage__._local.__ident_func__()このリストはスタック構造をシミュレートします.インタフェースpush,pop,topでは,このlistを操作することでスタックの操作をシミュレートするが,インタフェース関数内部でこのlistを取得する際には,上の黒体ほど複雑ではなく,直接_localのgetattr()メソッドでいいです.push関数を例にとると、次のように実現されます.
    def push(self, obj):
        """Pushes a new item to the stack"""
        rv = getattr(self._local, 'stack', None)
        if rv is None:
            self._local.stack = rv = []
        rv.append(obj)
        return rv

    popおよびtopの実装は、一般的なスタックと同様に、stack = getattr(self._local, 'stack', None)というリストに対して対応する動作を行う.さらに、LocalStackでは__ident_func__をカスタマイズできます.ここでは内蔵関数propertyで記述器を生成し、__をカプセル化します.ident_func__のgetとset操作で、属性値__が提供されます.ident_func__インタフェースとして、具体的なコードは以下の通りです.
    def _get__ident_func__(self):
        return self._local.__ident_func__
    
    def _set__ident_func__(self, value):
        object.__setattr__(self._local, '__ident_func__', value)
    __ident_func__ = property(_get__ident_func__, _set__ident_func__)
    del _get__ident_func__, _set__ident_func__

    LocalManager
    LocalとLocalStackは、スレッドまたはコモンシップが独立した単一のオブジェクトです.多くの場合、複数のLocalオブジェクトまたはLocalStackオブジェクトを組織するために、1つのリストで複数のintまたはstringタイプを組織するように、1つのスレッドまたはコモンシップが独立したコンテナが必要です.
    Werkzeugは、管理するLocalオブジェクトまたはLocalStackオブジェクトをlistタイプのプロパティlocalsで格納し、cleanupメソッドを使用してすべてのLocalオブジェクトを解放するLocalManagerを実現します.WerkzeugにおけるLocalManagerの最も主要なインタフェースは装飾方法make_middlewareであり、コードは以下の通りである.
    def make_middleware(self, app):
        """Wrap a WSGI application so that cleaning up happens after
        request end.
        """
        def application(environ, start_response):
            return ClosingIterator(app(environ, start_response), self.cleanup)
        return application

    このアクセラレータはコールバック関数cleanupを登録し、1つのスレッド(またはコモン)が要求を処理した後、cleanupを呼び出して管理されているLocalまたはLocalStackオブジェクト(ClosingIteratorの実装はwerkzeug.wsgi)をクリーンアップします.次に、LocalManagerを使用する簡単な例を示します.
    from werkzeug.local import Local, LocalManager
    
    local = Local()
    local_2 = Local()
    local_manager = LocalManager([local, local2])
    
    def application(environ, start_response):
        local.request = request = Request(environ)
        ...
    
    # application      ,     local_manager    
    application = local_manager.make_middleware(application)

    LocalManagerからのmake_middlewareでは、あるスレッド(スレッド)でリクエストを処理した後、すべてのLocalオブジェクトまたはLocalStackオブジェクトを空にすることで、このスレッドが別のリクエストを処理することができます.これで,文章の最初に述べた2つ目の問題が解決される.Werkzeug.LocalにはLocalオブジェクトのエージェントとしてLocalProxyが実装されており、学ぶ価値もあります.
    この3つの文章を通じて、ThreadLocalに対して初歩的な理解があると信じています.Python標準ライブラリとWerkzeugは実装に多くのpythonの黒魔法を使用しているが、最終的にはユーザーに非常に友好的なインタフェースを提供している.WerkzeugはWSGIツールセットとして、Web開発における特定の使用問題を解決するために、改良されたバージョンを提供し、一連のパッケージを行い、使いやすいようにしています.werkzeugのコードは可読性が非常によく、注釈も素晴らしいと言わざるを得ません.ソースコードを読むことをお勧めします.
    本文はselfbootが個人ブログに発表し、署名-非商業的使用-同じ方法で3.0中国大陸許可協定を共有した.非商業転載は作者と出典を明記してください.PythonのThreadLocal変数を深く理解する(下)このリンクは次のとおりです.http://selfboot.cn/2016/11/03...
    詳細
    Context Locals Private Variables and Class-local References How does the @property decorator work? How do the Proxy, Decorator, Adapter, and Bridge Patterns differ?
    Flaskソースの分析localsモジュール解読Charming Python:FlaskのrequestからHow to remove a key from a python dictionary?werkzeugソース分析(local.py)