回答:
リスト自体はスレッドセーフです。CPythonでは、GILはそれらへの同時アクセスから保護します。他の実装では、リストの実装に細粒度ロックまたは同期データ型を使用するように注意しています。ただし、リスト自体は同時にアクセスしようとしても破損することはありませんが、リストのデータは保護されません。例えば:
L[0] += 1
は+=
アトミック操作ではないため、別のスレッドが同じことを行った場合、実際にL [0]を1だけ増やすことは保証されません。(非常に少数のPythonの操作は実際にはアトミックです。それらのほとんどは任意のPythonコードが呼び出される可能性があるためです。)保護されていないリストを使用するだけの場合、競合のために間違ったアイテムを取得または削除する可能性があるため、キューを使用する必要があります。条件。
Thomasの優れた答えのポイントを明確にするために、スレッドセーフであることに言及する必要がappend()
あります。
これは、一度書き込んだデータが読み込まれると同じ場所にあるという心配がないためです。操作はそれだけでリストにデータを書き込み、データを読み取ることはありません。append()
PyList_Append
が1つのGILロックで行われます。追加するオブジェクトへの参照が与えられます。そのオブジェクトの内容は、評価された後、呼び出しPyList_Append
が行われる前に変更される可能性があります。しかし、それはまだ同じオブジェクト、および安全(あなたがしなければ追加されますlst.append(x); ok = lst[-1] is x
、そしてok
もちろん、Falseの場合もあります)。参照するコードは、INCREFを除いて、追加されたオブジェクトからは読み取りません。追加されたリストを読み取り、再割り当てします。
L[0] += x
実行する__getitem__
上でL
、その後__setitem__
にL
あれば- L
サポート__iadd__
は、オブジェクト・インターフェースで違うことを少し行いますが、二つの別々の操作がで残っているL
Pythonインタプリタのレベルで(あなたがそれらを見ることができますコンパイルされたバイトコード)。これappend
は、バイトコードの単一のメソッド呼び出しで行われます。
remove
ですか?
以下に、list
操作の例と、それらがスレッドセーフかどうかを包括的かつ網羅的なリストで示します。ここでobj in a_list
言語構成に関する答えを得たいと思っています。
私は最近、1つのスレッドで継続的にリストに追加し、アイテムをループしてアイテムの準備ができているかどうかを確認する必要があり、私の場合はAsyncResultであり、準備ができている場合にのみリストから削除する必要がありました。問題を明確に示す例は見つかりませんでした。あるスレッドのリストに継続的に追加し、別のスレッドの同じリストから継続的に削除する例を次に示します。欠陥のあるバージョンは、小さい数で簡単に実行できますが、数を十分に大きくして数回、あなたはエラーが表示されます
FLAWEDバージョン
import threading
import time
# Change this number as you please, bigger numbers will get the error quickly
count = 1000
l = []
def add():
for i in range(count):
l.append(i)
time.sleep(0.0001)
def remove():
for i in range(count):
l.remove(i)
time.sleep(0.0001)
t1 = threading.Thread(target=add)
t2 = threading.Thread(target=remove)
t1.start()
t2.start()
t1.join()
t2.join()
print(l)
エラー時に出力
Exception in thread Thread-63:
Traceback (most recent call last):
File "/Users/zup/.pyenv/versions/3.6.8/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/Users/zup/.pyenv/versions/3.6.8/lib/python3.6/threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "<ipython-input-30-ecfbac1c776f>", line 13, in remove
l.remove(i)
ValueError: list.remove(x): x not in list
ロックを使用するバージョン
import threading
import time
count = 1000
l = []
lock = threading.RLock()
def add():
with lock:
for i in range(count):
l.append(i)
time.sleep(0.0001)
def remove():
with lock:
for i in range(count):
l.remove(i)
time.sleep(0.0001)
t1 = threading.Thread(target=add)
t2 = threading.Thread(target=remove)
t1.start()
t2.start()
t1.join()
t2.join()
print(l)
出力
[] # Empty list
結論
以前の回答で述べたように、リスト自体から要素を追加またはポップする動作はスレッドセーフですが、スレッドセーフでないのは、あるスレッドに追加して別のスレッドにポップする場合です。
with r:
明示的にr.acquire()
andを呼び出すのではなく、コンテキストマネージャ()を使用する方がよいr.release()