Windows下multiprocessing Pool搭配Lock实现共享计数器失效如何解决
问题根源
这个问题是Windows和*nix系统多进程启动机制差异导致的:
- *nix系统默认用
fork模式启动子进程,会直接拷贝父进程的全部内存空间,所以父进程定义的lock、running变量子进程可以直接访问,你最初的写法可以正常运行。 - Windows系统默认用
spawn模式启动子进程,会重新导入当前运行的模块,if __name__ == '__main__'包裹的代码子进程不会执行,所以第一种写法里子进程找不到lock变量抛出NameError。如果把lock、running移到全局作用域,每个子进程导入模块时都会单独创建一份新的lock和running实例,本质已经不是父进程的共享对象了,所以锁失效、计数器值混乱,出现负数重复输出的问题。
解决方案
用multiprocessing.Pool的initializer参数,在子进程初始化的时候把共享的锁和计数器传入子进程的全局空间即可,可跨平台正常运行的代码如下:
import multiprocessing def init_globals(shared_lock, shared_running): # 子进程启动时执行初始化,把共享变量绑定到子进程的全局作用域 global lock, running lock = shared_lock running = shared_running def f(x): global running, lock # 你的业务逻辑写在这里 with lock: running.value -= 1 print(f"Still running: {running.value}\n", end='', flush=True) return x if __name__ == '__main__': lock = multiprocessing.Lock() rangeval = range(100) running = multiprocessing.Value('i', len(rangeval)) # 初始化进程池时传入初始化函数和共享参数 pool = multiprocessing.Pool( processes=multiprocessing.cpu_count(), initializer=init_globals, initargs=(lock, running) ) result = pool.map(f, iterable=rangeval) pool.close() pool.join()
注意事项
所有需要跨进程共享的同步对象、共享内存对象,都不要直接在全局作用域创建,要在父进程的if __name__ == '__main__'块中创建,再通过初始化参数传给子进程,避免Windows下子进程单独创建实例失去共享属性。
内容的提问来源于stack exchange,提问作者Vance Pyton
相关产品推荐
相关产品推荐

