CPythonでもスレッドセーフではありません。自分で見るために、これを試してみてください:
import threading
class Foo(object):
instance_count = 0
def inc_by(n):
for i in xrange(n):
Foo.instance_count += 1
threads = [threading.Thread(target=inc_by, args=(100000,)) for thread_nr in xrange(100)]
for thread in threads: thread.start()
for thread in threads: thread.join()
print(Foo.instance_count) # Expected 10M for threadsafe ops, I get around 5M
理由は(dis.dis(フー.__ init__)を参照)INPLACE_ADDはGILの下で、原子である一方で、属性がまだロードされ、ストアされていることです。ロックを使用してクラス変数へのアクセスをシリアル化します。
Foo.lock = threading.Lock()
def interlocked_inc(n):
for i in xrange(n):
with Foo.lock:
Foo.instance_count += 1
threads = [threading.Thread(target=interlocked_inc, args=(100000,)) for thread_nr in xrange(100)]
for thread in threads: thread.start()
for thread in threads: thread.join()
print(Foo.instance_count)
あなたの2番目の例では、スレッドターゲットをinc_byの代わりにinterlocked_incにしたいと考えています。 – tgray
ありがとうございました。あまりにもリベラルなコピーアンドペーストプログラミングは時々私と一緒に追いつく。 –
Ants Aasmaありがとう:-)これは私が疑った通りです。私にそれを証明してくれてありがとう。 tgrayが指摘するように、2番目のターゲットはinterlocked_incでなければなりません。しかし、一度それを変更すると...完璧に見えます。 – Tom