Python NumPy数组操作+=是否线程安全?

5
我怎么确定这段代码是线程安全的?
import numpy as np
from threading import Thread

n_threads = 5

ones = np.ones((5, 5))
A = np.ones((5, 5))

def my_function():
    global A
    for i in range(250):
        A += ones # is += thread safe ?

threads = [Thread(target=my_function) for i in range(n_threads)]

for t in threads:
    t.start()

for t in threads:
    t.join()

print(A)

是否应该将A设为关键共享内存?令人惊讶的是,我总是得到相同的结果,并且数组的所有条目都具有相同的值。我原本期望线程会更新矩阵的值,但实际上可能会有一些值丢失...

谢谢。


在我看来,即使在一个线程中也不安全。当数组有“许多”行(无论“许多”是什么意思)时,它们经常以块更新,因此如果您执行a+=a[0],则a[0]可能会随着您的操作而改变。 - DYZ
1
@DYZ:实际上,他们在 1.13 中引入了这种操作的定义语义,这是最新版本。 - user2357112
1
虽然GIL确实为CPython上的此类操作提供了一定程度的保护,但仍有一些情况会失败,而其他Python实现(其中一些对NumPy具有某种程度的支持)可能不进行锁定或更细粒度的锁定。 - user2357112
如果您希望将值重复添加到索引值中,则“+=”不是很有用。它使用缓冲区。请参见“add.at”以获取无缓冲版本。 - hpaulj
感谢您的澄清。 - Mário Costa
1个回答

3

你的代码不安全。一些NumPy ufuncs(如你隐式调用的add()函数)可能会释放GIL。也许你从未在玩具示例中看到任何问题的原因是它运行的时间非常短,数据量非常小。你可能还通过简单的计算本质来避免问题,但我想象你的真实代码更加复杂。

简而言之,如果没有锁定,你不能在多个线程中执行你正在执行的操作。而对于像这样的代码,锁定可能会挫败使用多个线程的目的。


1
当然,你是对的。我已经将矩阵大小增加到1000,迭代次数也是1000,问题就出现了。谢谢! - Mário Costa

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