4

このコードについて考えてみてください。

#!/usr/bin/env python

from threading import Thread

count = 0

def test():
    global count
    for i in range(10):
        count = count + 1

if __name__ == '__main__':
    for i in range(1000):
        Thread(target = test).start()
    print count

複数のスレッドを使用していますが、結果は常に正しいです。それは、ビジターカウンターのようなものを実装するときに、ロックなしでPythonスレッドを使用できることを意味しますか?

4

3 に答える 3

6

必要です。マルチスレッドの動作はPythonでは異なりますが、グローバルインタープリターロックにより、Pythonバイトコードでアトミックでない操作でもロックが必要になります。

あなたの場合、あなたはあなたの関数のバイトコードを調べることができますtestdis.dis(test)):

 3           0 SETUP_LOOP              30 (to 33)
             3 LOAD_GLOBAL              0 (range)
             6 LOAD_CONST               1 (1000)
             9 CALL_FUNCTION            1
            12 GET_ITER
       >>   13 FOR_ITER                16 (to 32)
            16 STORE_FAST               0 (i)

 4          19 LOAD_GLOBAL              1 (count)   # start of increment
            22 LOAD_CONST               2 (1)
            25 BINARY_ADD
            26 STORE_GLOBAL             1 (count)   # end of increment
            29 JUMP_ABSOLUTE           13
       >>   32 POP_BLOCK
       >>   33 LOAD_CONST               0 (None)
            36 RETURN_VALUE

ご覧のとおり、増分は2xload、update、storeでバイトコードレベルであるため、これは機能しません。増分は実際には4つの個別の操作であり、中断されないように保護する必要があります。

あなたの例ではcount += 1、バイトコードが示すように、を使用しても問題は残ります。

4          19 LOAD_GLOBAL              1 (count)
           22 LOAD_CONST               2 (1)
           25 INPLACE_ADD
           26 STORE_GLOBAL             1 (count)
于 2012-11-26T08:41:19.993 に答える
1

割り当てを行っただけの場合は、ロックは必要ありません。

しかし、あなたがそうするように、読み取り、追加、書き込みcount = count + 1のそれぞれの間に何かが起こる可能性があります。count1count

これには割り当ても含まれるため、使用してもcount += 1この問題は解決されません。(インプレース操作には内部での割り当ても含まれるため、状況は同じです。)

于 2012-11-26T08:52:42.847 に答える
0

間違いなく、ロックを使用する必要があります。このような単純なケースでは、正しい答えが得られます。mainのrange(100000)に合わせてみてください。問題が表示されます。私のマシンでは、結果は999960ですが、ランダムな結果です。システム負荷などによりエラーが発生します。

于 2012-11-26T08:51:12.220 に答える