Python3.10 下为什么没有多线程自增安全问题了?

import threading


a = 0


def func(index):
    global a
    # with threading.Lock():
    #     for i in range(10000):
    #         a+=1
    for i in range(1000000):
        a = a+1
    print(index, a)


t_1 = threading.Thread(target=func, args=[1])
t_1.start()


t_2 = threading.Thread(target=func, args=[2])
t_2.start()


t_3 = threading.Thread(target=func, args=[2])
t_3.start()

t_1.join()
t_2.join()
t_3.join()
print('全局', a)

一个简单的三线程累加的代码(不加锁)

使用 python3.10 运行,结果都是 3000000 ✅

─➤  python3.10 001.py
1 2632601
2 2767037
2 3000000
全局 3000000

使用 python3.9 运行,结果不是 3000000 ❓

─➤  python3.9 001.py
1 661812
2 1137151
2 1650809
全局 1650809

使用 python3.9 运行,结果不是 3000000 ❓

─➤  python3.8 001.py
2 1036512
2 1251107
1 1545036
全局 1545036

为什么 python3.10 下为什么没有多线程自增安全问题了?

这个变化没有在发行日志里面看到呀?

后背是做了什么导致的?

自动对自增操作加锁了?但是这样对于不需要多线程的场景不就变成累赘了吗?

阅读 2.9k
2 个回答

扩写一下 @张京 的答案。

用 python 自己的 dis 看一下 opcode ,就知道无论是 x+=1 还是 x=x+1 ,做加法 (INPLACE_ADD, BINARY_ADD) 跟保存结果 (STORE_FAST) 都是两条 opcode 。

python 代码执行过程中,只有在主动检测中断的地方才可能发生线程切换。原来在很多 opcode 之后都去检测中断,修改后只有少数的指令才会去检测中断了。比如,原来在 INPLACE_ADD 或 BINARY_ADD 之后都可能发生线程切换,但是修改后,这两条 opcode 之后不会发生线程切换了。

由于在 xxx_ADD 与 STORE_FAST 之间不会有线程切换了,所以看起来 x = x + 1 变得线程安全了。(LOAD_FAST, LOAD_COST 这两个 opcode ,原来就没有检测中断)

这个修改本身好像是一个 bug fix ,但是不是针对这个线程安全问题的。对这个线程安全问题的影响只是一个副作用。 python 本身没有对这里的线程安全做出保证,说不准到以后某个版本它又不安全了。需要线程安全,还是要加锁。

>>> def a(x): x+=1
...
>>> import dis
>>> dis.dis(a)
  1           0 LOAD_FAST                0 (x)
              2 LOAD_CONST               1 (1)
              4 INPLACE_ADD
              6 STORE_FAST               0 (x)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
>>> def a(x): x=x+1
...
>>> import dis
>>> dis.dis(a)
  1           0 LOAD_FAST                0 (x)
              2 LOAD_CONST               1 (1)
              4 BINARY_ADD
              6 STORE_FAST               0 (x)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
>>>
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
1 篇内容引用
推荐问题
宣传栏