Python多进程Semaphore异常:并发控制不符预期且跨平台不一致
问题分析与解决方案
你的代码存在两个核心问题,导致跨平台表现不一致且不符合预期:
1. 语法错误:if __name__判断写错
你写的if __name__ = "___main__"是赋值语句而非判断语句,且___main__多了一个下划线(正确应为__main__)。这个错误会导致:
- 在Linux的
fork模式下,子进程会意外执行模块顶层的进程创建代码,引发逻辑混乱; - 在MacOS/Windows的
spawn模式下,虽然不会直接引发进程爆炸,但会破坏信号量的共享逻辑。
2. 信号量未跨进程共享
你在全局作用域定义Sema,但不同平台的多进程启动机制会导致信号量无法共享:
- Linux(默认
fork):子进程继承父进程内存,但因if __name__错误,子进程会重新执行模块代码,导致信号量被重复初始化,最终出现串行执行的异常; - MacOS(Python3.8+默认
spawn):子进程会重新导入整个模块,每个子进程都会创建独立的Semaphore(3)实例,每个信号量都允许3次acquire,但单个进程只需要1次,因此10个进程可以同时执行,出现一次性输出所有starting!的情况。
修正后的代码
使用multiprocessing.Manager创建跨进程共享的信号量,并通过参数传递给子进程,同时修正if __name__判断:
import multiprocessing import time def test_print(sema): sema.acquire() print("starting!") time.sleep(5) print("ending!") sema.release() if __name__ == "__main__": concurrency = 3 # 使用Manager创建跨平台共享的信号量 with multiprocessing.Manager() as manager: shared_sema = manager.Semaphore(concurrency) process_list = [] for _ in range(10): p = multiprocessing.Process(target=test_print, args=(shared_sema,)) p.start() process_list.append(p) # 等待所有进程执行完毕 for p in process_list: p.join()
为什么这样能解决问题
Manager.Semaphore是真正跨进程共享的,无论fork还是spawn模式,所有子进程都会使用同一个信号量实例;- 正确的
if __name__ == "__main__"确保只有主进程会创建子进程,避免子进程重复执行模块代码; - 通过
args传递信号量,而非依赖全局变量,确保子进程使用的是主进程创建的共享实例,符合你的预期(最多3个进程同时执行)。
内容的提问来源于stack exchange,提问作者r_phys
相关产品推荐
相关产品推荐

