web-dev-qa-db-ja.com

Pythonインスタンス変数はスレッドセーフですか?

まず、次のコードを確認してください:

_class DemoClass():

    def __init__(self):
        #### I really want to know if self.Counter is thread-safe. 
        self.Counter = 0

    def Increase(self):
        self.Counter = self.Counter + 1

    def Decrease(self):
        self.Counter = self.Counter - 1

    def DoThis(self):
        while True:
            Do something

            if A happens:
                self.Increase()
            else:
                self.Decrease()

            time.sleep(randomSecs)

    def DoThat(self):
        while True:
            Do other things

            if B happens:
                self.Increase()
            else:
                self.Decrease()

            time.sleep(randomSecs)

    def ThreadSafeOrNot(self):
        InterestingThreadA = threading.Thread(target = self.DoThis, args = ())
        InterestingThreadA.start()

        InterestingThreadB = threading.Thread(target = self.DoThat, args = ())
        InterestingThreadB.start()
_

上記と同じ状況です。 _self.Counter_に対してスレッドセーフかどうか本当に知りたいのですが、そうでない場合、どのようなオプションがありますか?私はthreading.RLock()だけを考えて、このリソースをロックすることができます。

22
Shane

ロック、RLock、セマフォ、条件、イベント、およびキューを使用できます。
そしてこの記事は私を助けてくれましたたくさん
確認してください: Laurent Luceのブログ

33
aayoubi

インスタンスフィールドself.Counterの使用は スレッドセーフまたは「アトミック」 です。それを読み取るか、single値を割り当てる-メモリに4バイトが必要な場合でも、半分変更された値を取得することはありません。しかし、操作self.Counter = self.Counter + 1は、値を読み取ってから書き込むためではありません。別のスレッドが、読み取られてから書き戻される前に、フィールドの値を変更する可能性があります。

したがって、操作全体をロックで保護する必要があります。

メソッド本体は基本的に操作全体なので、デコレータを使用してこれを行うことができます。例については、この回答を参照してください: https://stackoverflow.com/a/490090/34088

27
Aaron Digulla

いいえ、それはスレッドセーフではありません。2つのスレッドは基本的に同じ変数を同時に変更しています。そして、はい、解決策はthreadingモジュールのロックメカニズムの1つです。

ところで、self.Counterインスタンス変数であり、クラス変数ではありません。

10
Eli Bendersky

_self.Counter_はインスタンス変数であるため、各スレッドにコピーがあります。

__init__()の外で変数を宣言すると、それはクラス変数になります。クラスのすべてのインスタンスがそのインスタンスを共有します。

3
Jack Mason