【发布时间】:2021-03-14 19:49:37
【问题描述】:
我有N 独立任务在multiprocessing.Pool 大小os.cpu_count()(在我的例子中为8)和maxtasksperchild=1 中执行(即为每个新任务创建一个新的工作进程)。
主脚本可以简化为:
import subprocess as sp
import multiprocessing as mp
def do_work(task: dict) -> dict:
res = {}
# ... work ...
for i in range(5):
out = sp.run(cmd, stdout=sp.PIPE, stderr=sp.PIPE, check=False, timeout=60)
res[i] = out.stdout.decode('utf-8')
# ... some more work ...
return res
if __name__ == '__main__':
tasks = load_tasks_from_file(...) # list of dicts
logger = mp.get_logger()
results = []
with mp.Pool(processes=os.cpu_count(), maxtasksperchild=1) as pool:
for i, res in enumerate(pool.imap_unordered(do_work, tasks), start=1):
results.append(res)
logger.info('PROGRESS: %3d/%3d', i, len(tasks))
dump_results_to_file(results)
游泳池有时会卡住。我执行KeyboardInterrupt 时的回溯是here。
它表示池不会获取新任务和/或工作进程卡在队列/管道recv() 调用中。我无法确定地重现这个,改变我的实验的不同配置。如果我再次运行相同的代码,它可能会优雅地完成。
进一步观察:
- x64 Linux 上的 Python 3.7.9
- 多处理的启动方法是
fork(使用spawn不能解决问题) -
strace表明进程卡在futex wait中; gdb 的回溯还显示:do_futex_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
【问题讨论】:
-
您确定
subprocess.run调用完成了吗?这将阻塞直到子进程完成,所以如果它没有...... -
是的,
subprocess.run完成。当死锁发生时,没有其他工作子进程被报告(例如在htop) - 都处于可中断睡眠状态。 -
您使用的是什么操作系统和 Python 版本?默认启动方法因各种选项而异(在明显的嫌疑人中,在 3.8 之前,macOS 使用
fork,但 it caused problems,所以 3.8 and higher switched tospawnby default),并且可能与您的问题有关。即使在分叉系统上,您也可能想要更改启动方法;fork如果父级很大并且子级执行垃圾收集(导致写入时复制,导致内存使用高峰),则可能会出现问题。 -
你可以尝试将进程限制为 1,看看它是否仍然与大型任务列表死锁?
-
如果 not 使用
maxtasksperchild解决了您的问题,您可以尝试升级到 Python 3.8,工作人员管理方面发生了变化。
标签: python multiprocessing deadlock