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 问题,如果出现问题的函数尚不存在问题,请打开一个新问题。