4

想想这段代码:

#!/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 线程?

4

3 回答 3

6

你确实需要一个。尽管多线程在 Python 中的工作方式不同,但由于Global Interpreter Lock,Python 字节码中非原子的操作仍然需要锁定。

在你的情况下,你可以检查你的函数testdis.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、update、store,所以这不起作用。增量实际上是 4 个单独的操作,您必须保护它们以确保它们不会被中断。

在您的示例中,即使您使用 ,问题仍然存在count += 1,如字节码所示:

4          19 LOAD_GLOBAL              1 (count)
           22 LOAD_CONST               2 (1)
           25 INPLACE_ADD
           26 STORE_GLOBAL             1 (count)
于 2012-11-26T08:41:19.993 回答
1

如果你只是做作业,你就不需要锁。

但是当你这样做count = count + 1时,在每次读取count、添加1和写入之间可能会发生一些事情count

即使使用count += 1也不能解决这个问题,因为这也涉及到分配。(由于就地操作也涉及到后台分配,所以情况是一样的。)

于 2012-11-26T08:52:42.847 回答
0

当然你应该使用锁。在这种简单的情况下,您已经得到了正确的答案。尝试在main中设置 range(100000) 。你会看到问题。在我的机器中,结果是 999960,但它是随机结果。根据系统负载等情况会出现错误。

于 2012-11-26T08:51:12.220 回答