我正在使用Dask运行任务池,按照as_completed
方法完成的顺序检索结果,并且每次返回时都可能向池中提交新任务:
# Initial set of jobs
futures = [client.submit(job.run_simulation) for job in jobs]
pool = as_completed(futures, with_results=True)
while True:
# Wait for a job to finish
f, result = next(pool)
# Exit condition
if result == 'STOP':
break
# Do processing and maybe submit more jobs
more_jobs = process_result(f, result)
more_futures = [client.submit(job.run_simulation) for job in more_jobs]
pool.update(more_futures)
这是我的问题:我提交的函数job.run_simulation
有时会挂起很长时间,我希望超时这个功能 - 如果运行时间超过一定的时间限制,请终止任务并继续
理想情况下,我想执行类似client.submit(job.run_simulation, timeout=10)
的操作,如果任务的运行时间超过超时,则next(pool)
会返回None
。
Dask有什么方法可以帮我节省这样的工作时间吗?
到目前为止我尝试了什么
我的第一直觉是在job.run_simulation
函数本身内独立于Dask处理超时。我已经看到了两种类型的建议(例如here)用于通用Python超时。
1)使用两个线程,一个用于函数本身,另一个用于计时器。我的印象是,这实际上并不起作用,因为你无法杀死线程。即使计时器用完,两个线程也必须在任务完成之前完成。
2)使用两个单独的进程(使用multiprocessing
模块),一个用于函数,一个用于计时器。这可行,但由于我已经在Dask生成的守护进程子进程中,我不允许创建新的子进程。
第三种可能性是将代码块移动到我使用subprocess.run
运行的单独脚本,并使用内置超时的subprocess.run
。我可以做到这一点,但感觉就像是一个最糟糕的回退场景,因为它会在子进程中传递大量繁琐的数据。
所以感觉我必须在Dask级别完成超时。我的一个想法是在将任务提交给Dask的同时创建一个定时器作为子进程。然后,如果计时器用完,请使用Client.cancel()
停止任务。这个计划的问题是Dask可能会等待工作人员在启动任务之前释放,并且我不希望在任务实际运行之前运行计时器。
答案 0 :(得分:0)
您对问题的评估对我来说似乎是正确的,您所经历的解决方案与我考虑的相同。一些说明:
Client.cancel
无法停止运行。这些函数在线程池中运行,因此您遇到了“无法停止线程”的限制。 Dask worker只是Python进程,具有相同的能力和限制。您说您无法在守护程序进程中使用进程。对此的一个解决方案是通过以下方式之一更改您使用流程的方式:
如果你在一台机器上使用dask.distributed,那就不要使用进程
client = Client(processes=False)
multiprocessing-context
配置设置为"spawn"
而不是fork或forkserver 解决这个问题的干净方法是在函数job.run_simulation
内解决它。理想情况下,您可以将此超时逻辑推送到该代码并使其干净地提升。