如何将提交给Dask的工作超时?

时间:2018-04-19 15:42:53

标签: python dask

我正在使用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可能会等待工作人员在启动任务之前释放,并且我不希望在任务实际运行之前运行计时器。

1 个答案:

答案 0 :(得分:0)

您对问题的评估对我来说似乎是正确的,您所经历的解决方案与我考虑的相同。一些说明:

    如果函数已经启动,则
  1. Client.cancel无法停止运行。这些函数在线程池中运行,因此您遇到了“无法停止线程”的限制。 Dask worker只是Python进程,具有相同的能力和限制。
  2. 您说您无法在守护程序进程中使用进程。对此的一个解决方案是通过以下方式之一更改您使用流程的方式:

    • 如果你在一台机器上使用dask.distributed,那就不要使用进程

      client = Client(processes=False)
      
    • 不要使用Dask的默认保姆进程,那么你的dask worker将是一个能够使用多处理的正常进程
    • 将dask的multiprocessing-context配置设置为"spawn"而不是fork或forkserver
  3. 解决这个问题的干净方法是在函数job.run_simulation内解决它。理想情况下,您可以将此超时逻辑推送到该代码并使其干净地提升。