Python 多处理池中偶尔出现死锁

Python 多处理池中偶尔出现死锁,python,multiprocessing,deadlock,Python,Multiprocessing,Deadlock,我有N独立任务,它们在多处理.Pool中执行,大小os.cpu\u count()(在我的例子中为8),并且maxstasksperchild=1(即为每个新任务创建一个新的工作进程) 主脚本可以简化为: 将子流程作为sp导入 将多处理作为mp导入 def do_工作(任务:dict)->dict: res={} # ... 工作 对于范围(5)中的i: out=sp.run(cmd,stdout=sp.PIPE,stderr=sp.PIPE,check=False,timeout=60) re

我有
N
独立任务,它们在
多处理.Pool
中执行,大小
os.cpu\u count()
(在我的例子中为8),并且
maxstasksperchild=1
(即为每个新任务创建一个新的工作进程)

主脚本可以简化为:

将子流程作为sp导入
将多处理作为mp导入
def do_工作(任务:dict)->dict:
res={}
# ... 工作
对于范围(5)中的i:
out=sp.run(cmd,stdout=sp.PIPE,stderr=sp.PIPE,check=False,timeout=60)
res[i]=out.stdout.decode('utf-8')
# ... 还有一些工作。。。
返回res
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
任务=从文件(…)#目录列表中加载任务
logger=mp.get\u logger()
结果=[]
将mp.Pool(processs=os.cpu\u count(),maxstasksperchild=1)作为池:
对于i,枚举中的res(pool.imap_无序(do_work,tasks),start=1):
结果追加(res)
logger.info('进度:%3d/%3d',i,len(任务))
将\u结果\u转储到\u文件(结果)
游泳池有时会卡住。执行
键盘中断时的回溯是。
它表示池不会获取新任务和/或工作进程卡在队列/管道
recv()
调用中。我无法通过改变我的实验的不同配置,决定性地再现这一点。如果我再次运行相同的代码,它可能会优雅地完成

进一步的意见:

  • x64 Linux上的Python 3.7.9
  • 多处理的启动方法是
    fork
    (使用
    spawn
    无法解决此问题)
  • strace
    显示进程卡在
    futex等待中
    ;gdb的回溯也显示:
    do\u futex\u wait.constprop
  • 禁用日志记录/显式刷新没有帮助
  • 任务的定义方式没有任何缺陷(即它们都是可加载的)

更新:似乎即使池大小为1,也会发生死锁

strace
报告进程在尝试获取位于
0x564c5dbcd000的某个锁时被阻止:

futex(0x564c5dbcd000, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, NULL, FUTEX_BITSET_MATCH_ANY
gdb
确认:

(gdb) bt
#0  0x00007fcb16f5d014 in do_futex_wait.constprop () from /usr/lib/libpthread.so.0
#1  0x00007fcb16f5d118 in __new_sem_wait_slow.constprop.0 () from /usr/lib/libpthread.so.0
#2  0x0000564c5cec4ad9 in PyThread_acquire_lock_timed (lock=0x564c5dbcd000, microseconds=-1, intr_flag=0)
    at /tmp/build/80754af9/python_1598874792229/work/Python/thread_pthread.h:372
#3  0x0000564c5ce4d9e2 in _enter_buffered_busy (self=self@entry=0x7fcafe1e7e90)
    at /tmp/build/80754af9/python_1598874792229/work/Modules/_io/bufferedio.c:282
#4  0x0000564c5cf50a7e in _io_BufferedWriter_write_impl.isra.2 (self=0x7fcafe1e7e90)
    at /tmp/build/80754af9/python_1598874792229/work/Modules/_io/bufferedio.c:1929
#5  _io_BufferedWriter_write (self=0x7fcafe1e7e90, arg=<optimized out>)
    at /tmp/build/80754af9/python_1598874792229/work/Modules/_io/clinic/bufferedio.c.h:396
(gdb)bt
#来自/usr/lib/libpthread.so.0的do_futex_wait.constprop()中的0 0x00007fcb16f5d014
#1 0x00007fcb16f5d118,位于/usr/lib/libpthread.so.0中的uu_new_sem_wait_slow.constprop.0()中
#PyThread中的2 0x0000564c5cec4ad9获取锁定已定时(锁定=0x564c5dbcd000,微秒=-1,intr_标志=0)
at/tmp/build/80754af9/python_1598874792229/work/python/thread_pthread.h:372
#3 0x0000564c5ce4d9e2输入缓冲区忙(自=self@entry=0x7fcafe1e7e90)
at/tmp/build/80754af9/python_1598874792229/work/Modules/_io/bufferedio.c:282
#4 0x0000564c5cf50a7e在I/O缓冲写入程序I/U写入执行isra.2中(self=0x7fcafe1e7e90)
at/tmp/build/80754af9/python_1598874792229/work/Modules/_io/bufferedio.c:1929
#5 io_BufferedWriter_write(self=0x7fcafe1e7e90,arg=)
at/tmp/build/80754af9/python_1598874792229/work/Modules/_io/clinic/bufferedio.c.h:396

死锁是由于工作进程内存使用率高而发生的,因此触发了OOM killer,它突然终止了工作子进程,使池处于混乱状态

重现我原来的问题

目前,我正在考虑切换到一个,当突然发生工作者终止时,它将抛出一个
BrokenProcessPool
异常

参考资料:


您确定
子流程.run
调用已完成吗?这将一直阻止,直到子流程完成,因此如果它没有…是,
subprocess.run
finishs。死锁发生时,不会报告工作进程的其他子进程(例如,在
htop
),所有子进程都处于可中断睡眠状态。您使用的是什么操作系统和Python版本?默认的启动方法在不同的选项中有所不同(在3.8版本之前,macOS使用了
fork
,但是,因此,在明显的可疑选项中),并且可能与您的问题有关。即使在分叉系统上,也可能需要更改启动方法<如果父进程较大,而子进程执行垃圾收集(导致写时复制,导致内存使用率激增),则代码>分叉可能会出现问题。您能否尝试将进程限制为1,并查看它是否仍然会因较大的任务列表而死锁?如果不使用
maxtasksparchild
解决问题,您可以尝试升级到Python 3.8,工人管理方面发生了变化。