5

別の質問でのいくつかの議論は、マルチスレッドPythonプログラムでロックが必要な場合をよりよく理解するように私を促しました。

Pythonでのスレッド化に関するこの記事によると、複数のスレッドが共有状態にアクセスするときに発生する可能性のある落とし穴の確かでテスト可能な例がいくつかあります。このページで提供されている競合状態の例には、辞書に格納されている共有変数を読み取って操作するスレッド間の競合が含まれます。ここでのレースのケースは非常に明白であり、幸いにも非常にテスト可能だと思います。

ただし、リストの追加や変数の増分などの不可分操作で競合状態を引き起こすことはできませんでした。このテストは、そのようなレースを徹底的に実証しようとします。

from threading import Thread, Lock
import operator

def contains_all_ints(l, n):
    l.sort()
    for i in xrange(0, n):
        if l[i] != i:
            return False
    return True

def test(ntests):
    results = []
    threads = []
    def lockless_append(i):
        results.append(i)
    for i in xrange(0, ntests):
        threads.append(Thread(target=lockless_append, args=(i,)))
        threads[i].start()
    for i in xrange(0, ntests):
        threads[i].join()
    if len(results) != ntests or not contains_all_ints(results, ntests):
        return False
    else:
        return True

for i in range(0,100):
    if test(100000):
        print "OK", i
    else:
        print "appending to a list without locks *is* unsafe"
        exit()

上記のテストを失敗することなく実行しました(100x 100kマルチスレッド追加)。誰かがそれを失敗させることができますか?スレッドによるアトミック、インクリメンタル、変更を介して誤動作させることができる別のクラスのオブジェクトはありますか?

これらの暗黙の「アトミック」セマンティクスは、Pythonの他の操作に適用されますか?これはGILに直接関係していますか?

4

2 に答える 2

7

リストへの追加はスレッドセーフです、はい。リストに追加できるのはGILを保持している間だけであり、リストはappend操作中にGILを解放しないように注意します(つまり、かなり単純な操作です)。異なるスレッドの追加操作が実行される順序は次のとおりです。取得するためのコースですが、GILが追加中に解放されることはないため、これらはすべて厳密にシリアル化された操作になります。

同じことが他の操作にも必ずしも当てはまりません。Pythonで多くの操作を行うと、任意のPythonコードが実行され、GILが解放される可能性があります。たとえばi += 1、「get i」、「add 1 to it」、「store itin」の3つの異なる操作がありiます。「add1toit」は、(この場合)に変換されit.__iadd__(1)、好きなように実行できます。

Pythonオブジェクト自体が自身の内部状態を保護します。つまり、2つの異なるスレッドがアイテムを設定しようとして、dictが破損することはありません。しかし、dictのデータが内部的に一貫していると想定される場合、dictもGILもそれを保護するために何もしませ

于 2010-04-29T20:17:03.720 に答える
1

CPythonでは、sys.getcheckinteval()バイコードが実行されたときにスレッドの切り替えが行われます。したがって、単一のバイトコードの実行中にコンテキストスイッチが発生することはなく、単一のバイトコードとしてエンコードされる操作は、そのバイトコードが他のPythonコードを実行するか、GILを解放するCコードを呼び出さない限り、本質的にアトミックでスレッドセーフです。組み込みのコレクションタイプ(dict、listなど)に対するほとんどの操作は、「本質的にスレッドセーフ」なカテゴリに分類されます。

ただし、これはPythonのC実装に固有の実装の詳細であり、信頼するべきではありません。他のバージョンのPython(Jython、IronPython、PyPyなど)は同じように動作しない場合があります。また、CPythonの将来のバージョンがこの動作を維持するという保証もありません。

于 2010-04-29T20:34:41.420 に答える