Python多进程并发与同步机制详解:Process、同步原语与Manager

Hazel
Hazel
发布时间:2026-10-16| 17人浏览过
原创

本文深入解析Python中multiprocessing模块的使用,涵盖Process类的创建与属性、僵尸进程的处理策略、Lock/Event等同步机制以及Manager共享数据对象。通过具体代码示例,展示如何高效利用多核CPU并避免常见并发陷阱。

前文《多线程与同步》探讨了多线程及其局限性,而采用多进程方式可有效规避全局解释器锁(GIL)的限制。借助multiprocessing模块,程序能够充分利用多核CPU的计算资源。

多进程基础

在Python中,多进程功能由multiprocessing包提供。其中,multiprocessing.Process对象(与threading.Thread类似)用于创建一个新的进程实例。

  • 在类UNIX系统平台上,必须对每个Process对象调用join()方法(其作用等同于wait),以防止子进程在父进程退出后变为僵尸进程。
  • multiprocessing提供了threading模块所不具备的进程间通信(IPC)机制,如Pipe和Queue,且效率更高。建议优先使用Pipe和Queue进行通信,尽量避免使用Lock、Event、Semaphore或Condition等同步原语。
  • 多进程编程中应尽量避免共享资源。若确需共享,可通过共享内存(Shared Memory)或Manager对象来实现资源共享。

僵尸进程处理

在Unix或类Unix系统中,当子进程结束时,若父进程未通过wait系统调用读取其退出状态,该子进程将保持僵尸状态,持续占用部分系统资源且无法释放。

清除僵尸进程主要有以下几种方法:

终止父进程:当父进程(通常为主进程)退出时,其所有的僵尸子进程也会被操作系统自动清理。

读取子进程退出状态:对于由multiprocessing.Process创建的进程,可通过以下方式处理:

  • 调用join()方法等待子进程结束(该方法内部会自动执行wait系统调用);
  • 在父进程中处理SIGCHLD信号:在信号处理程序中调用wait系统调用,或者将SIGCHLD信号设置为SIG_IGN以忽略该信号,从而自动清理僵尸进程;

使进程成为孤儿进程:若父进程先于子进程退出,子进程将被init进程收养,由init自动处理其状态。

通过设定signal.signal(signal.SIGCHLD, signal.SIG_IGN)或调用join()方法,可以有效避免僵尸进程的产生。

def zombieProc():
    print("zombie running")
    time.sleep(5)
    print("zombie exit")
if __name__ == '__main__':
    signal.signal(signal.SIGCHLD, signal.SIG_IGN)
    proc = multiprocessing.Process(target=zombieProc)
    proc.start()
    # proc.join()
    time.sleep(30)

Process类详解

Process([group [, target [, name [, args [, kwargs]]]]]),实例化后得到的对象代表一个子进程任务:

  • group参数目前未被使用,值始终为None;
  • target表示调用的可调用对象,即子进程需要执行的具体任务;
  • args表示传递给调用对象的位置参数元组,例如args=(1, 'test', ['one']);
  • kwargs表示传递给调用对象的字典参数,例如kwargs={'name':'mike','age':18};
  • name用于指定子进程的名称;

Process类包含以下主要属性与方法:

  • start():启动进程,并自动执行其run方法;
  • run():进程启动时执行的方法,若继承Process类,则必须实现此方法;
  • terminate():强制终止进程,不执行任何清理操作(若进程创建了子进程,子进程将变为僵尸进程);如果进程持有锁等资源,这些资源也不会被释放,可能导致死锁;
  • is_alive():返回布尔值,指示进程是否仍在运行;
  • join([timeout]):阻塞父进程,直到子进程终止;
  • daemon:默认值为False。若设为True,表示该进程为守护进程(父进程终止时它也会终止,且不能创建新子进程),必须在start()之前设置;
  • name:进程的名称;
  • pid/ident:进程的ID;
  • exitcode:进程运行时为None;若为-N,表示进程被信号N终止;
  • authkey:进程的身份验证密钥(默认由os.urandom()生成的32字符字符串),在涉及网络连接的底层进程间通信中提供安全性保障;

也可通过os.getpid()获取当前进程的PID,通过os.getppid()获取父进程的PID。

函数方式创建进程

通过Process类直接运行函数示例如下:

def simpleRoutine(name, delay):
    print(f"routine {name} starting...")
    time.sleep(delay)
    print(f"routine {name} finished")
if __name__ == '__main__':
    thrOne = multiprocessing.Process(target=simpleRoutine, args=("First", 1))
    thrTwo = multiprocessing.Process(target=simpleRoutine, args=("Two", 2))
    thrOne.start()
    thrTwo.start()
    thrOne.join()
    thrTwo.join()

继承方式创建进程

通过继承Process类并实现run方法来启动进程:

class SimpleProcess(multiprocessing.Process):
    def __init__(self, name, delay):
        super().__init__()
        self.name = name
        self.delay = delay
    def run(self):
        print(f"Process {self.name} starting...")
        time.sleep(self.delay)
        print(f"Process {self.name} finished")
if __name__ == '__main__':
    thrOne = SimpleProcess("First", 2)
    thrTwo = SimpleProcess("Second", 1)
    thrOne.start()
    thrTwo.start()
    thrOne.join()
    thrTwo.join()

同步机制

进程间的同步机制与线程间类似(所有对象均位于multiprocessing模块中):

  • Lock/Rlock:通过acquire()和release()方法获取与释放锁;
  • Event:事件信号,通过set()和clear()方法设定与清除信号;通过wait()方法等待信号;
  • Condition:条件变量;通过wait()等待条件,通过notify/notify_all()通知等待该条件的进程(等待与通知前需先持有锁);
  • Semaphore:信号量;维护一个计数器以限制并发访问数量;
  • Barrier:屏障;只有当等待进程数量达到指定数量时,才会同时执行屏障保护后的代码。

屏障使用示例:

def waitBarrier(name, barr: multiprocessing.Barrier):
    print(f"{name} waiting for open")
    try:
        barr.wait()
        print(f"{name} running")
        time.sleep(2)
    except multiprocessing.BrokenBarrierError:
        print(f"{name} exception")
    print(f"{name} finished")
def openFun():  # 屏障满足条件时,执行一次
    print("barrier opened")
if __name__ == '__main__':
    signal = multiprocessing.Barrier(5, openFun)
    for i in range(10):
        multiprocessing.Process(target=waitBarrier, args=(i, signal)).start()  
        time.sleep(1)  

当第5个进程启动时,前5个进程会同时继续执行(openFun函数执行一次);当第10个进程启动时,后5个进程会同时继续执行一次(openFun函数再次执行)。

状态管理Manager

Managers提供了一种在多进程(包括跨机器进程共享)中创建共享数据的方式:

  • multiprocessing.Manager()返回一个SyncManager对象;该对象对应一个管理者子进程(manager process)以及代理对象(其他子进程通过代理访问);
  • 它确保当一个进程修改了共享对象后,其他进程中的共享对象也能同步更新;
  • 其支持的类型包括:list、dict、Namespace、Lock、RLock、Semaphore、BoundedSemaphore、Condition、Event、Queue、Value和Array。

多进程共享字典与列表(每个进程中都能看到其他进程修改后的内容)

def worker(dictContext: dict, lstContext: list, name):
    pid = os.getpid()
    dictContext[name] = pid
    lstContext.append(pid)
    print(f"{name} worker: {lstContext}")
def managerContext():
    mgr = multiprocessing.Manager()
    multiprocessing.managers
    dictContext = mgr.dict()
    lstContext = mgr.list()
    jobs = [multiprocessing.Process(target=worker, args=(dictContext, lstContext, i)) for i in range(10)]
    for j in jobs:
        j.start()
    for j in jobs:
        j.join()
    print('Results:', dictContext)

本文关于Python多进程并发与同步机制的详细讲解到此结束,更多相关Python多进程内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。