多处理模块

from __future__ import print_function
import multiprocessing

def countdown(count):
    while count > 0:
        print("Count value", count)
        count -= 1
    return

if __name__ == "__main__":
    p1 = multiprocessing.Process(target=countdown, args=(10,))
    p1.start()

    p2 = multiprocessing.Process(target=countdown, args=(20,))
    p2.start()

    p1.join()
    p2.join()

这里,每个函数都在一个新进程中执行。由于 Python VM 的新实例正在运行代码,因此没有 GIL,你可以在多个内核上运行并行操作。

Process.start 方法启动这个新进程,并使用参数 args 运行 target 参数中传递的函数。Process.join 方法等待进程 p1p2 的执行结束。

根据 python 的版本和运行代码的平台形式,新进程的启动方式不同,例如

  • Windows 使用 spawn 来创建新进程。
  • 对于 unix 系统和早于 3.3 的版本,使用 fork 创建流程。
    请注意,此方法不考虑 fork 的 POSIX 使用,因此会导致意外行为,尤其是在与其他多处理库交互时。
  • 使用 unix 系统和 3.4+版本,你可以选择在程序开始时使用 multiprocessing.set_start_method 启动 forkforkserverspawn 的新流程。forkserverspawn 方法比分叉慢,但避免一些意想不到的行为。

POSIX fork 用法

在多线程程序中的 fork 之后,子进程可以安全地只调用异步信号安全函数,直到它调用 execve 为止。

使用 fork,将为所有当前互斥锁启动一个具有完全相同状态的新进程,但只会启动 MainThread。这是不安全的,因为它可能导致竞争条件,例如

  • 如果你在 MainThread 中使用 Lock 并将其传递给另一个线程,该线程可能会在某个时刻将其锁定。如果 fork 同时发生,则新进程将以锁定锁启动,该锁将永远不会释放,因为此新进程中不存在第二个线程。

实际上,这种行为不应该在纯 python 中发生,因为 multiprocessing 正确处理它,但如果你正在与其他库交互,这种行为可能会发生,导致你的系统崩溃(例如在 macOS 上有 numpy /加速)。