You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

省略锁后Python多进程未出现预期竞态问题的原因

问题与原因分析

你编写了跨多进程共享的数据库连接池代码,未在get()方法中加锁,原本预期会因conn = self.ready_pool[-1]和self.ready_pool.pop()的非原子操作引发竞态,导致多个进程获取到同一ID的连接,但实际运行后11个进程里10个成功获取连接、1个失败,并未出现重复连接的情况,核心原因如下:

关键:multiprocessing.Manager的自动同步机制

你通过multiprocessing.managers.BaseManager托管DatabaseConnectionPool对象,Manager的工作逻辑决定了不会出现竞态:

  • Manager会启动一个独立的服务进程来管理所有注册的共享对象
  • 其他进程对共享对象的任何方法调用,都要通过进程间通信(IPC)转发到服务进程执行
  • Manager默认会为每个方法调用自动添加互斥锁,确保整个方法的执行是原子性的——也就是说,当一个进程在执行pool.get()时,其他进程的get()调用会被阻塞,直到前一个调用完全执行完毕。

哪怕你自己没在get()方法里加锁,Manager已经替你完成了方法级别的同步,整个get()方法的执行是串行的,不会出现多个进程同时读取并弹出连接的情况,自然不会有重复获取连接的问题。11个进程里10个成功1个失败,完全符合连接池最大10个连接的设定。

代码示例

from multiprocessing import Process, Lock
from multiprocessing.managers import BaseManager


class DatabaseConnection:
    def __init__(self, conn_id):
        self.id = conn_id

    def __repr__(self):
        return f'connection(id={self.id})'

class DatabaseConnectionPool:
    def __init__(self):
        self.mutex = Lock()
        self.max_connections = 10
        self.ready_pool = [DatabaseConnection(i) for i in range(self.max_connections)]
        self.leased_pool = []

    def get(self):
        if not self.ready_pool:
            raise Exception('no connections available')
    
        conn = self.ready_pool[-1]
        self.ready_pool.pop()
        self.leased_pool.append(conn)

        return conn


class Manager(BaseManager): pass

Manager.register('DatabaseConnectionPool', DatabaseConnectionPool)

def proc(pool):
    try:
        conn = pool.get()
        print(f'leased {conn}')
    except:
        print(f'failed to lease connection')

if __name__ == '__main__':

    manager = Manager()
    manager.start()
    pool = manager.DatabaseConnectionPool()

    procs = []
    for _ in range(11):
        p = Process(target=proc, args=(pool,))
        procs.append(p)
        p.start()

    for p in procs:
        p.join()

运行结果

leased connection(id=9)
leased connection(id=8)
leased connection(id=7)
leased connection(id=6)
leased connection(id=5)
leased connection(id=4)
leased connection(id=3)
leased connection(id=2)
leased connection(id=1)
leased connection(id=0)
failed to lease connection

内容的提问来源于stack exchange,提问作者mlz7

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 11:20:28