Python 3.10与其他版本的线程有何不同?

28

对于一些简单的线程相关代码,例如:

import threading


a = 0
threads = []


def x():
    global a
    for i in range(1_000_000):
        a += 1


for _ in range(10):
    thread = threading.Thread(target=x)
    threads.append(thread)
    thread.start()


for thread in threads:
    thread.join()


print(a)
assert a == 10_000_000

我们会根据Python版本获得不同的行为。
对于3.10,输出如下:
❯ python3.10 b.py
10000000

对于3.9版本,输出结果为:
❯ python3.9 b.py
2440951
Traceback (most recent call last):
  File "/Users/romka/t/threads-test/b.py", line 24, in <module>
    assert a == 10_000_000
AssertionError

由于我们没有获取任何锁定,所以对我来说,3.9的结果是显而易见的和预期的。问题是为什么和如何3.10获得了“正确”的结果,而不应该呢?

我正在审查Python 3.10的变更日志,没有任何与线程或GIL相关的内容可以带来这样的结果。


我不知道确切的答案,但根据 https://dev59.com/tXI-5IYBdhLWcg3wtaxq,Python 解释器应该只在每 X 条操作码之后切换活动线程。因此,可能是他们改变了 X 或者 += 语句使用了不同数量的操作码,这意外地使其线程安全。 - Tin Tvrtković
https://twitter.com/yhg1s/status/1460935209059328000?s=21 - Alex Waygood
2
有趣的测试:将 a += 1 更改为 a += int(1) 将会导致预期的较低数字再次出现。 - Kelly Bundy
1个回答

25

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接