python multiprocess踩坑记录:在进程池multiprocess.Pool模块中使用锁Lock
需求:开启多个独立运行的子进程(1k+),子进程中可能出现异常,但不知道报什么错,要求子进程出错不妨碍其他进程继续执行,结束之后能看到哪些进程出了错并整合成日志。
不使用Lock造成print重叠
下面是一个50%概率出现除0错误的进程
def raise_error(i):
try:
rand=random.randrange(0,2)
ans=10/rand
time.sleep(1)
print(os.getpid(),':',i,'sucess result is',ans,'parent',os.getppid())
return ans
except Exception as e:
print(os.getpid(),':',i,'failed',e,'parent',os.getppid())
return -1
使用multiprocess.Pool模块调用,进程池大小为3,总共10个进程
if __name__ == '__main__':
print('Parent process %s.' % os.getpid())
pool=multiprocessing.Pool(processes=3)
for i in range(10):
pool.apply_async(func=raise_error,args=(i,))
pool.close()
pool.join()
print('done')
有概率出现以下结果:
可以看到输出有重叠,原因在于三个进程同时往缓冲区string buffer中写数据造成stdout未按顺序输出(往相同total log中输出同理),所以需要对子进程中的print进行加锁
加锁遇到的问题
根据官方文档以及众多博客所写的进行加锁
def lock_raise_error(i,l):
try:
rand=random.randrange(0,2)
ans=10/rand
l.acquire()#加锁
print(os.getpid(),':',i,'sucess result is',ans)
l.release()
return ans
except Exception as e:
print(os.getpid(),'failed',e)
return -1
if __name__ == '__main__':
print('Parent process %s.' % os.getpid())
pool=multiprocessing.Pool(processes=3)
lock=multiprocessing.Lock()
for i in range(10):
a=pool.apply_async(func=lock_raise_error,args=(i,lock))
pool.close()
pool.join()
print(a.successful())
print('done')
结果如下,并没有执行
一开始还以为是因为args里面元组出现问题,改成args=(i,lock,)之后依旧没有执行,原因大概在于官方文档和博文中都是用Process模块开启多线程,再使用Lock加锁,但Pool模块无法使用这种方法加锁
使用Manager对其加锁
参考https://blog.csdn.net/u013713010/article/details/53325438
的方法使用manager加锁
def lock_raise_error(i,l):
try:
rand=random.randrange(0,2)
ans=10/rand
time.sleep(1)
l.acquire()
print(os.getpid(),':',i,'sucess result is',ans)
l.release()
return ans
except Exception as e:
print(os.getpid(),'failed',e)
return -1
if __name__ == '__main__':
print('Parent process %s.' % os.getpid())
pool=multiprocessing.Pool(processes=3)
lock=multiprocessing.Manager().Lock()#使用Manager加锁
for i in range(10):
a=pool.apply_async(func=lock_raise_error,args=(i,lock))
pool.close()
pool.join()
print(a.successful())
print('done')
输出没有出现重叠现象,加锁成功,输出到total日志文件同理
与https://blog.csdn.net/u013713010/article/details/53325438不同的是,我在直接使用Lock而非Manage().Lock()时,并没有出现RuntimeError。
使用Pool时除了Lock需要在Manager中初始化还有Queue,Value等。