120

特に Python では、スレッド間で変数がどのように共有されるのでしょうか?

以前に使用したthreading.Threadことはありますが、変数がどのように共有されるかを本当に理解したり、例を見たりしたことはありませんでした。それらはメインスレッドと子の間で共有されますか、それとも子の間でのみ共有されますか? この共有を避けるためにスレッド ローカル ストレージを使用する必要があるのはいつですか?

ロックを使用してスレッド間で共有データへのアクセスを同期することに関する多くの警告を見てきましたが、問題の本当に良い例をまだ見ていません。

前もって感謝します!

4

6 に答える 6

105

Python では、関数ローカル変数を除いてすべてが共有されます (各関数呼び出しは独自のローカル セットを取得し、スレッドは常に個別の関数呼び出しであるため)。さらに、変数自体 (オブジェクトを参照する名前) のみが共有されます。関数に対してローカルです。オブジェクト自体は常にグローバルであり、何でも参照できます。このThread点で、特定のスレッドのオブジェクトは特別なオブジェクトではありません。Threadすべてのスレッドがアクセスできる場所 (グローバル変数など) にオブジェクトを格納すると、すべてのスレッドがその 1 つのThreadオブジェクトにアクセスできます。別のスレッドがアクセスできるものをアトミックに変更したい場合は、ロックで保護する必要があります。もちろん、すべてのスレッドがこのまったく同じロックを共有する必要があります。そうしないと、あまり効果的ではありません。

実際のスレッド ローカル ストレージが必要な場合は、ここでthreading.local出番です。 の属性はthreading.localスレッド間で共有されません。各スレッドは、それ自体がそこに配置した属性のみを認識します。その実装に興味がある場合、ソースは標準ライブラリの_threading_local.pyにあります。

于 2008-09-19T19:59:40.587 に答える
89

次のコードを検討してください。

#/usr/bin/env python

from time import sleep
from random import random
from threading import Thread, local

data = local()

def bar():
    print("I'm called from", data.v)

def foo():
    bar()

class T(Thread):
    def run(self):
        sleep(random())
        data.v = self.getName()   # Thread-1 and Thread-2 accordingly
        sleep(1)
        foo()
>> T().start(); T().start()
Thread-2 から呼び出されました
Thread-1 から呼び出されました

ここで、threading.local() は、foo() のインターフェースを変更せずに run() から bar() にデータを渡すための迅速かつ汚い方法として使用されます。

グローバル変数を使用してもうまくいかないことに注意してください。

#/usr/bin/env python

from time import sleep
from random import random
from threading import Thread

def bar():
    global v
    print("I'm called from", v)

def foo():
    bar()

class T(Thread):
    def run(self):
        global v
        sleep(random())
        v = self.getName()   # Thread-1 and Thread-2 accordingly
        sleep(1)
        foo()
>> T().start(); T().start()
Thread-2 から呼び出されました
Thread-2 から呼び出されました

一方、このデータを foo() の引数として渡す余裕がある場合は、よりエレガントで適切に設計された方法になります。

from threading import Thread

def bar(v):
    print("I'm called from", v)

def foo(v):
    bar(v)

class T(Thread):
    def run(self):
        foo(self.getName())

ただし、サードパーティのコードや設計が不十分なコードを使用している場合、これが常に可能であるとは限りません。

于 2009-12-12T18:58:50.557 に答える
26

を使用してスレッド ローカル ストレージを作成できますthreading.local()

>>> tls = threading.local()
>>> tls.x = 4 
>>> tls.x
4

tls に保存されるデータは各スレッドに固有であり、意図しない共有が発生しないようにするのに役立ちます。

于 2008-09-20T00:31:24.037 に答える
3

他のすべての言語と同様に、Pythonのすべてのスレッドは同じ変数にアクセスできます。「メインスレッド」と子スレッドの区別はありません。

Pythonとの違いの1つは、グローバルインタープリターロックは、一度に1つのスレッドのみがPythonコードを実行できることを意味することです。ただし、アクセスの同期に関しては、これはあまり役に立ちません。ただし、通常のプリエンプションの問題はすべて引き続き適用され、他の言語と同じようにスレッドプリミティブを使用する必要があります。ただし、パフォーマンスのためにスレッドを使用していたかどうかを再検討する必要があることを意味します。

于 2008-09-19T20:03:30.640 に答える
1

言及する価値threading.local()があるのはシングルトンではありません。

スレッドごとにより多くのそれらを使用できます。1つのストレージではありません。

于 2021-12-22T14:15:00.913 に答える