如何在Python应用程序中检测数据竞争?

2024-04-25 07:55:37 发布

您现在位置:Python中文网/ 问答频道 /正文

目前还不清楚可以使用什么工具来检测python脚本、python代码或基于python的可执行文件中可能出现的数据竞争

我知道至少对于CPython,只允许对GIL执行一个线程。但这并不意味着不能发生数据竞争(例如,当两个线程在一个结构上写入一个属性时,每个线程都有一个值)

那么,我怎样才能检测到这种情况呢


Tags: 工具数据代码脚本可执行文件属性情况cpython
1条回答
网友
1楼 · 发布于 2024-04-25 07:55:37

假设您已经完成了明显的第一步,并查看了非原子操作的代码,例如共享变量上的v += 1

您可以通过增加线程删除GIL的频率来扩展显示竞态条件的窗口。您可以通过将切换间隔从默认的5ms降低到5ms来完成此操作。您还可以在代码中的可疑区域周围插入time.sleep(0),以便在另一个线程试图获取GIL时,让线程将GIL放在此处。另外,随机插入的睡眠时间(“模糊化”)也是进一步扭曲线程执行顺序的一个选项

因为可能不是每个人都清楚这样做的意义:

您的代码可能会正常工作,因为线程可以轻松地在5毫秒的标准时间片中遍历关键部分,并且您在测试过程中获得了操作系统调度的“幸运”。后来,您添加了一些代码,关键部分突然出现在时间片的边缘,您可以通过奇怪的bug来注意竞争条件

在更改^{}的帮助下显示竞争条件的演示代码:

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

相关问题 更多 >