想想这段代码:
#!/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线程?
答案 0 :(得分:6)
你确实需要一个。虽然多线程在Python中的工作方式不同,但由于Global Interpreter Lock,Python字节码中非原子的操作仍然需要锁定。
在这种情况下,您可以检查函数test
(dis.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,更新,存储在字节码级别,因此这不起作用。增量实际上是4个单独的操作,您必须保护它们以确保它们不被中断。
在您的示例中,即使您使用count += 1
,问题仍然存在,因为字节码显示:
4 19 LOAD_GLOBAL 1 (count)
22 LOAD_CONST 2 (1)
25 INPLACE_ADD
26 STORE_GLOBAL 1 (count)
答案 1 :(得分:1)
如果您刚刚完成作业,则不需要锁定。
但正如您count = count + 1
所做的那样,在阅读count
,添加1
和写入count
之间可能会发生一些事情。
即使使用count += 1
也无法解决此问题,因为这也涉及到一项任务。 (由于现场操作也涉及到引擎下的任务,情况也是如此。)
答案 2 :(得分:0)
绝对应该使用Lock。在这么简单的情况下,你得到了正确的答案。尝试在主要中设置范围(100000)。你会看到问题。在我的机器中,结果是999960,但它是随机结果。根据系统负载等情况会出现错误。