我正在尝试学习 Python 3.12.2 的教程。我正在尝试演示非线程安全代码的部分。该教程表示以下代码将产生不可预测的结果。嗯,对我来说,它产生了非常可预测的结果。代码是:
# when no thread synchronization used
from threading import Thread as Thread
def inc():
global x
for _ in range(1000000):
x+=1
#global variable
x = 0
counter = 0
while counter < 10:
# creating threads
threads = [Thread(target=inc) for _ in range(10)]
# start the threads
for thread in threads:
thread.start()
#wait for the threads
for thread in threads:
thread. Join()
print("Pass ", counter, "final value of x:", f"{x:,}")
x = 0
counter += 1
它产生了以下输出;
PS D:\PythonDev> python .\thread3a.py
Pass 0 final value of x: 10,000,000
Pass 1 final value of x: 10,000,000
Pass 2 final value of x: 10,000,000
Pass 3 final value of x: 10,000,000
Pass 4 final value of x: 10,000,000
Pass 5 final value of x: 10,000,000
Pass 6 final value of x: 10,000,000
Pass 7 final value of x: 10,000,000
Pass 8 final value of x: 10,000,000
Pass 9 final value of x: 10,000,000
PS D:\PythonDev>
我修改了它以添加外部 while 循环,这样我就不必一遍又一遍地从命令行运行它。根据教程,每次传递的预期结果应该是 10,000,000。然而,实际上结果应该是不可预测的并且小于10,000,000。两者都不是。我做错了什么?
我的环境是;
O/S: MS Windows 10 Home 22H2
RAM: 16 GB
CPU: Intel Core I7-2860QM
Terminal Session: PowerShell 7.4.1
Python version: 3.12.2
尽管 Tim Roberts 发表了评论,但操作
x += 1
不是原子的;它由多个 Python 字节码操作组成(请参见下面的偏移量 12 到 18):
>>> import dis
>>> def inc():
... global x
... for _ in range(1000000):
... x+=1
...
>>> dis.dis(inc)
3 0 LOAD_GLOBAL 0 (range)
2 LOAD_CONST 1 (1000000)
4 CALL_FUNCTION 1
6 GET_ITER
>> 8 FOR_ITER 12 (to 22)
10 STORE_FAST 0 (_)
4 12 LOAD_GLOBAL 1 (x)
14 LOAD_CONST 2 (1)
16 INPLACE_ADD
18 STORE_GLOBAL 1 (x)
20 JUMP_ABSOLUTE 8
>> 22 LOAD_CONST 0 (None)
24 RETURN_VALUE
>>>
因此,当每个线程的时间片结束时,它将在任意字节码指令处被中断。当我运行代码时(用
thread. Join()
替换 thread.join()
后)我得到:
Pass 0 final value of x: 6,688,134
Pass 1 final value of x: 6,096,719
Pass 2 final value of x: 6,250,393
Pass 3 final value of x: 6,116,210
Pass 4 final value of x: 6,686,225
Pass 5 final value of x: 4,912,244
Pass 6 final value of x: 4,965,819
Pass 7 final value of x: 6,301,143
Pass 8 final value of x: 6,549,947
Pass 9 final value of x: 7,321,995
如果您有...
from threading import Thread as Thread, Lock
lock = Lock()
def inc():
global x
for _ in range(1000000):
with lock:
x+=1
...
...每次迭代你都会得到 10,000,000 -- 但代码运行速度要慢得多。