84

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

関連している

4

5 に答える 5

142

スレッドローカルストレージは、たとえば、スレッドワーカープールがあり、各スレッドがネットワークやデータベース接続などの独自のリソースにアクセスする必要がある場合に役立ちます。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回、関数呼び出しごとに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 __name__ == '__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とそれぞれのカウント、および順序は実行ごとに異なります。

于 2012-11-05T20:46:39.483 に答える
27

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

通常のオブジェクトと同じよう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も同様に機能します)。インスタンス変数またはローカル変数としてスレッドローカルストレージが必要になる場合がありますが、それらは比較的まれである(そしてかなり微妙である)傾向があります。

于 2015-04-21T19:49:46.097 に答える
17

質問で述べたように、AlexMartelliはここで解決策を示します。この関数を使用すると、ファクトリ関数を使用して各スレッドのデフォルト値を生成できます。

#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
于 2009-09-10T23:05:44.257 に答える
5

書くこともできます

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

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

于 2009-09-10T23:10:52.733 に答える
4

モジュール/ファイル間でスレッドローカルストレージを実行する私の方法。以下はPython3.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などの使用可能なデフォルト値を確認できます

于 2019-04-15T08:07:50.340 に答える