web-dev-qa-db-ja.com

Pythonのスレッドローカルストレージ

Pythonでスレッドローカルストレージを使用するにはどうすればよいですか?

関連する

59
Casebash

質問で述べたように、Alex Martelliはソリューションを提供します ここ 。この関数を使用すると、ファクトリ関数を使用して各スレッドのデフォルト値を生成できます。

#Code originally posted by Alex Martelli
#Modified to use standard Python variable name conventions
import threading
threadlocal = threading.local()    

def threadlocal_var(varname, factory, *args, **kwargs):
  v = getattr(threadlocal, varname, None)
  if v is None:
    v = factory(*args, **kwargs)
    setattr(threadlocal, varname, v)
  return v
16
Casebash

スレッドローカルストレージは、たとえば、スレッドワーカープールがあり、各スレッドがネットワークやデータベース接続などの独自のリソースにアクセスする必要がある場合に役立ちます。 threadingモジュールはスレッド(プロセスのグローバルデータにアクセスできる)の通常の概念を使用しますが、これらはグローバルインタープリターロックのためあまり有用ではないことに注意してください。異なるmultiprocessingモジュールはそれぞれに新しいサブプロセスを作成するため、グローバルはスレッドローカルになります。

スレッドモジュール

以下に簡単な例を示します。

_import threading
from threading import current_thread

threadLocal = threading.local()

def hi():
    initialized = getattr(threadLocal, 'initialized', None)
    if initialized is None:
        print("Nice to meet you", current_thread().name)
        threadLocal.initialized = True
    else:
        print("Welcome back", current_thread().name)

hi(); hi()
_

これは印刷されます:

_Nice to meet you MainThread
Welcome back MainThread
_

簡単に見落とされがちな1つの重要なこと:threading.local()オブジェクトは、スレッドごとまたは関数呼び出しごとに1回ではなく、1回だけ作成する必要があります。 globalまたはclassレベルは理想的な場所です。

その理由は次のとおりです。threading.local()は、呼び出されるたびに新しいインスタンスを実際に作成します(ファクトリまたはクラス呼び出しと同様)。したがって、threading.local()を複数回呼び出すと、元のオブジェクトが常に上書きされます。おそらく、それは望んでいるものではありません。スレッドが既存のthreadLocal変数(または呼び出されたもの)にアクセスすると、その変数の独自のプライベートビューを取得します。

これは意図したとおりには機能しません。

_import threading
from threading import current_thread

def wont_work():
    threadLocal = threading.local() #oops, this creates a new dict each time!
    initialized = getattr(threadLocal, 'initialized', None)
    if initialized is None:
        print("First time for", current_thread().name)
        threadLocal.initialized = True
    else:
        print("Welcome back", current_thread().name)

wont_work(); wont_work()
_

この出力になります:

_First time for MainThread
First time for MainThread
_

マルチプロセッシングモジュール

multiprocessingモジュールはスレッドごとに新しいプロセスを作成するため、すべてのグローバル変数はスレッドローカルです。

processedカウンターがスレッドローカルストレージの例である次の例を検討してください。

_from multiprocessing import Pool
from random import random
from time import sleep
import os

processed=0

def f(x):
    sleep(random())
    global processed
    processed += 1
    print("Processed by %s: %s" % (os.getpid(), processed))
    return x*x

if __== '__main__':
    pool = Pool(processes=4)
    print(pool.map(f, range(10)))
_

次のようなものが出力されます。

_Processed by 7636: 1
Processed by 9144: 1
Processed by 5252: 1
Processed by 7636: 2
Processed by 6248: 1
Processed by 5252: 2
Processed by 6248: 2
Processed by 9144: 2
Processed by 7636: 3
Processed by 5252: 3
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
_

...もちろん、スレッドIDとそれぞれのカウントと順序は実行ごとに異なります。

97
mbells

スレッドローカルストレージは、名前空間(属性表記を介して値にアクセスする)と見なすことができます。違いは、各スレッドが透過的に独自の属性/値のセットを取得するため、あるスレッドが別のスレッドからの値を認識しないことです。

通常のオブジェクトと同様に、コードに複数のthreading.localインスタンスを作成できます。ローカル変数、クラスまたはインスタンスメンバ、またはグローバル変数になります。それぞれが個別の名前空間です。

以下に簡単な例を示します。

import threading

class Worker(threading.Thread):
    ns = threading.local()
    def run(self):
        self.ns.val = 0
        for i in range(5):
            self.ns.val += 1
            print("Thread:", self.name, "value:", self.ns.val)

w1 = Worker()
w2 = Worker()
w1.start()
w2.start()
w1.join()
w2.join()

出力:

Thread: Thread-1 value: 1
Thread: Thread-2 value: 1
Thread: Thread-1 value: 2
Thread: Thread-2 value: 2
Thread: Thread-1 value: 3
Thread: Thread-2 value: 3
Thread: Thread-1 value: 4
Thread: Thread-2 value: 4
Thread: Thread-1 value: 5
Thread: Thread-2 value: 5

ns属性がクラスメンバーである(したがって、スレッド間で共有される)場合でも、各スレッドが独自のカウンターを保持する方法に注意してください。

同じ例では、インスタンス変数またはローカル変数を使用することもできますが、共有は行われないため、あまり表示されません(dictも同様に機能します)。インスタンス変数またはローカル変数としてスレッドローカルストレージが必要な場合もありますが、それらは比較的まれである(そしてかなり微妙な)傾向があります。

18
Paul Moore

書くこともできます

import threading
mydata = threading.local()
mydata.x = 1

mydata.xは現在のスレッドにのみ存在します

5
Casebash

スレッドローカルストレージを行う私の方法モジュール/ファイル間以下は、Python 3.5-

import threading
from threading import current_thread

# fileA.py 
def functionOne:
    thread = Thread(target = fileB.functionTwo)
    thread.start()

#fileB.py
def functionTwo():
    currentThread = threading.current_thread()
    dictionary = currentThread.__dict__
    dictionary["localVar1"] = "store here"   #Thread local Storage
    fileC.function3()

#fileC.py
def function3():
    currentThread = threading.current_thread()
    dictionary = currentThread.__dict__
    print (dictionary["localVar1"])           #Access thread local Storage

fileAで、別のモジュール/ファイルにターゲット関数を持つスレッドを開始します。

fileBで、そのスレッドに必要なローカル変数を設定します。

fileCで、現在のスレッドのスレッドローカル変数にアクセスします。

さらに、 'dictionary'変数を出力するだけで、kwargs、argsなどの利用可能なデフォルト値を確認できます

1
Shivansh Jagga