from threading import Thread
import sys
# import time
def foo(n):
global global_v
for _ in range(n):
x = global_v
for _ in range(100): # expand race condition for demo
pass
# time.sleep(0) # also: multiple locations with randomizing sleep times
global_v = x + 1
def run(n_workers, n_iter):
print(f"test with switch-interval: {sys.getswitchinterval()} sec")
pool = [Thread(target=foo, args=(n_iter,)) for _ in range(n_workers)]
for t in pool:
t.start()
for t in pool:
t.join()
print(f"expected: {n_workers * n_iter}, actual: {global_v}")
if __name__ == '__main__':
N_WORKERS = 8
n_iter = 1000
global_v = 0
# sys.setswitchinterval(0.001)
run(N_WORKERS, n_iter)
输出:
test with switch-interval: 0.005 sec
expected: 8000, actual: 8000
Process finished with exit code 0
带有sys.setswitchinterval(0.001)的输出:
test with switch-interval: 0.001 sec
expected: 8000, actual: 7318
Process finished with exit code 0
假设您已经完成了明显的第一步,并查看了非原子操作的代码,例如共享变量上的
v += 1
您可以通过增加线程删除GIL的频率来扩展显示竞态条件的窗口。您可以通过将切换间隔从默认的5ms降低到5ms来完成此操作。您还可以在代码中的可疑区域周围插入
time.sleep(0)
,以便在另一个线程试图获取GIL时,让线程将GIL放在此处。另外,随机插入的睡眠时间(“模糊化”)也是进一步扭曲线程执行顺序的一个选项因为可能不是每个人都清楚这样做的意义:
您的代码可能会正常工作,因为线程可以轻松地在5毫秒的标准时间片中遍历关键部分,并且您在测试过程中获得了操作系统调度的“幸运”。后来,您添加了一些代码,关键部分突然出现在时间片的边缘,您可以通过奇怪的bug来注意竞争条件
在更改^{} 的帮助下显示竞争条件的演示代码:
输出:
带有
sys.setswitchinterval(0.001)
的输出:相关问题 更多 >
编程相关推荐