SciPy 中的线程安全性#
SciPy 通过标准库中的 threading
模块支持在多线程上下文中使用。许多 SciPy 操作会释放 GIL,NumPy 也是如此(并且许多 SciPy 功能是通过调用 NumPy 函数实现的)——因此,与 Python 中的许多情况不同,可以通过利用 Python 中的多线程并行性来提高并行性能。
最简单的性能提升发生在每个工作线程拥有自己的数组或数组对象集,且线程之间没有直接共享数据时。大部分时间都在底层代码中运行的线程通常会并行运行。
可以在线程之间共享 NumPy 数组,但在修改多个线程之间共享的数组时,必须格外小心以避免产生线程安全问题——请参阅 NumPy 线程安全文档以获取更多详细信息。SciPy 函数不会修改用户传入的数组,除非函数明确说明会这样做(这种情况很少见)。因此,以多线程方式对同一个 NumPy 数组调用 SciPy 函数是安全的。
尽管 SciPy 大部分由 函数 组成,但对于 类 和 数据结构 需要格外小心。
具有状态的类,例如 scipy.integrate
和 scipy.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 问题,如果针对出现问题的函数尚未存在问题,请创建一个新问题。