线程安全#

NumPy 通过标准库中的 threading 模块支持在多线程环境中使用。许多 NumPy 操作会释放 GIL,因此与 Python 中的许多情况不同,可以通过利用 Python 中的多线程并行性来提高并行性能。

最容易实现的性能提升发生在每个工作线程拥有自己的数组或数组对象集合,且线程之间没有直接共享数据的情况下。由于 NumPy 会为许多底层操作释放 GIL,因此大部分时间花在底层代码中的线程将并行运行。

可以在线程之间共享 NumPy 数组,但当改变多个线程之间共享的数组时,必须格外小心,以避免创建线程安全问题。如果两个线程同时读写同一个数组,它们充其量会产生不一致、有竞争条件且不可复现的结果,更不用说正确性了。例如,当另一个线程正在读取数组以计算 ufunc 操作时,调整数组大小也可能导致 Python 解释器崩溃。

将来,我们可能会向 ndarray 添加锁,以使使用 NumPy 数组编写多线程算法更安全,但目前我们建议重点关注线程之间共享数组的只读访问,或者如果您需要进行改变和多线程操作,则添加您自己的锁。

请注意,释放 GIL 的操作将无法通过使用 threading 模块获得性能提升,反而可能更适合使用 multiprocessing。特别是,对 dtype=object 的数组进行操作不会释放 GIL。

自由线程 Python#

2.1 版本新增。

从 NumPy 2.1 和 CPython 3.13 开始,NumPy 也实验性地支持禁用 GIL 的 Python 运行时。有关安装和使用自由线程 Python 的更多信息,以及如何在依赖 NumPy 的库中支持它的信息,请参见 https://py-free-threading.github.io

由于自由线程 Python 没有全局解释器锁来序列化对 Python 对象的访问,因此线程有更多机会修改共享状态并创建线程安全问题。除了上面提到的 ndarray 对象加锁的限制外,这也意味着 dtype=object 的数组不受 GIL 保护,从而为 Python 对象创建了在自由线程 Python 之外不可能发生的数据竞争。