线程安全#

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 之外不可能出现的数据竞争。