不要指望x += 1 是线程安全的。这是 an example 不起作用的地方(请参阅 Josiah Carlson 的评论):
import threading
x = 0
def foo():
global x
for i in xrange(1000000):
x += 1
threads = [threading.Thread(target=foo), threading.Thread(target=foo)]
for t in threads:
t.daemon = True
t.start()
for t in threads:
t.join()
print(x)
如果你反汇编foo:
In [80]: import dis
In [81]: dis.dis(foo)
4 0 SETUP_LOOP 30 (to 33)
3 LOAD_GLOBAL 0 (xrange)
6 LOAD_CONST 1 (1000000)
9 CALL_FUNCTION 1
12 GET_ITER
>> 13 FOR_ITER 16 (to 32)
16 STORE_FAST 0 (i)
5 19 LOAD_GLOBAL 1 (x)
22 LOAD_CONST 2 (1)
25 INPLACE_ADD
26 STORE_GLOBAL 1 (x)
29 JUMP_ABSOLUTE 13
>> 32 POP_BLOCK
>> 33 LOAD_CONST 0 (None)
36 RETURN_VALUE
你看到有一个LOAD_GLOBAL来检索x的值,有一个INPLACE_ADD,然后是一个STORE_GLOBAL。
如果两个线程连续LOAD_GLOBAL,那么它们可能都加载x 的相同值。然后它们都递增到相同的数字,并存储相同的数字。所以一个线程的工作会覆盖另一个线程的工作。这不是线程安全的。
如您所见,如果程序是线程安全的,x 的最终值将是 2000000,但您几乎总是得到小于 2000000 的数字。
如果你添加一个锁,你会得到“预期”的答案:
import threading
lock = threading.Lock()
x = 0
def foo():
global x
for i in xrange(1000000):
with lock:
x += 1
threads = [threading.Thread(target=foo), threading.Thread(target=foo)]
for t in threads:
t.daemon = True
t.start()
for t in threads:
t.join()
print(x)
产量
2000000
我认为您发布的代码没有问题的原因:
for i in range(1000):
t = threading.Thread(target = worker)
threads.append(t)
t.start()
是因为你的workers 完成得如此之快,与产生一个新线程所需的时间相比,实际上线程之间没有竞争。在上面 Josiah Carlson 的示例中,每个线程都在 foo 中花费大量时间,这增加了线程冲突的机会。