SciPy 中的线程安全#

SciPy 通过标准库中的 threading 模块支持在多线程环境中使用。许多 SciPy 操作会释放 GIL,NumPy 也是如此(并且许多 SciPy 功能是通过调用 NumPy 函数实现的) - 因此,与 Python 中的许多情况不同,可以通过利用 Python 中的多线程并行性来提高并行性能。

当每个工作线程拥有自己的数组或一组数组对象,且线程之间没有直接共享数据时,最容易获得性能提升。大部分时间都花费在底层代码中的线程通常会并行运行。

可以在线程之间共享 NumPy 数组,但在修改在多个线程之间共享的数组时,必须格外小心,以避免创建线程安全问题 - 有关更多详细信息,请参阅 NumPy 关于线程安全的文档。SciPy 函数不会修改用户传入的数组,除非函数明确声明它会这样做(这种情况很少见)。因此,在同一个 NumPy 数组上以线程方式调用 SciPy 函数是安全的。

虽然 SciPy 的大部分内容都由函数组成,但对于数据结构必须更加小心。

具有状态的类,例如 scipy.integratescipy.interpolate 中的一些积分和插值对象,通常可以安全地并行调用。它们要么接受并行调用,要么引发有用的错误。例如,scipy.integrate.ode 可能会为不支持并行执行的积分方法引发 IntegratorConcurrencyError

SciPy 提供了一些数据结构,即 scipy.sparse 中的稀疏数组和矩阵,以及 scipy.spatial 中的 k-D 树。这些数据结构目前不是线程安全的。请特别避免在多个线程之间共享数据时,执行修改数据结构的操作,例如在稀疏数组上使用项或切片赋值。这可能会导致数据损坏、崩溃或其他不良行为。

请注意,不释放 GIL 的操作不会从使用 threading 模块中看到任何性能提升,而使用 multiprocessing 可能会更好。

无线程 Python#

在 1.15.0 版本中添加。

从 SciPy 1.15.0 和 CPython 3.13 开始,SciPy 对在所有平台上禁用 GIL 的 Python 运行时提供了实验性支持。有关安装和使用无线程 Python 的更多信息,请参阅 https://py-free-threading.github.io

由于无线程 Python 没有全局解释器锁 (GIL) 来序列化对 Python 对象的访问,因此线程有更多机会修改共享状态并创建线程安全问题。所有 SciPy 功能都经过并行线程使用的测试,但是我们预计会存在尚未发现的问题 - 如果您遇到问题,请检查 带有 free-threading 标签的 GitHub 问题,如果出现问题的函数尚不存在问题,请打开一个新问题。