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。
无 GIL Python#
版本 1.15.0 中新增。
从 SciPy 1.15.0 和 CPython 3.13 开始,SciPy 对所有平台上的禁用 GIL 的 Python 运行时提供实验性支持。 请参阅 https://py-free-threading.github.io 以获取有关安装和使用无 GIL Python 的更多信息。
由于无 GIL Python 没有全局解释器锁 (GIL) 来序列化对 Python 对象的访问,因此线程有更多机会修改共享状态并创建线程安全问题。 所有 SciPy 功能都经过测试,以确保可以从并行线程使用,但是我们预计仍存在尚未发现的问题 - 如果您遇到问题,请检查 带有 free-threading 标签的 GitHub issue,如果尚未为出现故障的函数创建新的 issue,请打开一个新的 issue。