Python:concurrent.futures如何使其可取消?

时间:2017-03-14 10:06:07

标签: python multiprocess concurrent.futures

Python concurrent.futures和ProcessPoolExecutor提供了一个简洁的界面来安排和监控任务。期货甚至provide a .cancel()方法:

  

取消():尝试取消通话。如果该呼叫当前正在执行且无法取消,则该方法将返回False,否则该呼叫将被取消,该方法将返回True。

不幸的是,在一个simmilar question(关于asyncio)中,回答声称运行任务是不可取消的,使用这个剪下来的文档,但文档不要说,只有当它们运行且不可执行时。

向进程提交multiprocessing.Events也是不可能的(通过参数执行此操作,如在multiprocess.Process中返回RuntimeError)

我想做什么?我想分区搜索空间并为每个分区运行任务。但它足以拥有一个解决方案,而且这个过程是CPU密集型的。那么,是否有一种实际可行的方法可以通过使用ProcessPool开始来抵消收益?

示例:

from concurrent.futures import ProcessPoolExecutor, FIRST_COMPLETED, wait

# function that profits from partitioned search space
def m_run(partition):
    for elem in partition:
        if elem == 135135515:
            return elem
    return False

futures = []
# used to create the partitions
steps = 100000000
with ProcessPoolExecutor(max_workers=4) as pool:
    for i in range(4):
        # run 4 tasks with a partition, but only *one* solution is needed
        partition = range(i*steps,(i+1)*steps)
        futures.append(pool.submit(m_run, partition))

    done, not_done = wait(futures, return_when=FIRST_COMPLETED)
    for d in done:
        print(d.result())

    print("---")
    for d in not_done:
        # will return false for Cancel and Result for all futures
        print("Cancel: "+str(d.cancel()))
        print("Result: "+str(d.result()))

3 个答案:

答案 0 :(得分:4)

不幸的是,无法取消正在运行Futures。我认为核心原因是在不同的实现上确保相同的API(它不可能中断正在运行的线程或协同程序)。

Pebble库旨在克服这个和其他限制。

from pebble import ProcessPool

def function(foo, bar=0):
    return foo + bar

with ProcessPool() as pool:
    future = pool.schedule(function, args=[1])

    # if running, the container process will be terminated 
    # a new process will be started consuming the next task
    future.cancel()  

答案 1 :(得分:2)

我发现你的问题很有趣,所以这是我的发现。

我发现applet.icns方法的行为与python文档中所述相同。至于你正在运行的并发功能,不幸的是,即使他们被告知这些功能也无法取消。如果我的发现是正确的,那么我认为Python确实需要更有效的.cancel()方法。

运行以下代码检查我的发现。

#{tag.date}

<强>更新 可以通过bash强制终止并发进程,但结果是主python程序也将终止。如果这不是您的问题,请尝试以下代码。

您必须在最后两个打印语句之间添加以下代码才能亲自查看。注意:只有在您没有运行任何其他python3程序时,此代码才有效。

.cancel()

答案 2 :(得分:2)

我不知道为什么concurrent.futures.Future没有.kill()方法,但您可以通过使用pool.shutdown(wait=False)关闭进程池并杀死剩余的子进程来完成所需的操作手工流程。

创建一个杀死子进程的函数:

import signal, psutil

def kill_child_processes(parent_pid, sig=signal.SIGTERM):
    try:
        parent = psutil.Process(parent_pid)
    except psutil.NoSuchProcess:
        return
    children = parent.children(recursive=True)
    for process in children:
        process.send_signal(sig)

运行代码,直到获得第一个结果,然后终止所有剩余的子进程:

from concurrent.futures import ProcessPoolExecutor, FIRST_COMPLETED, wait

# function that profits from partitioned search space
def m_run(partition):
    for elem in partition:
        if elem == 135135515:
            return elem
    return False

futures = []
# used to create the partitions
steps = 100000000
pool = ProcessPoolExecutor(max_workers=4)
for i in range(4):
    # run 4 tasks with a partition, but only *one* solution is needed
    partition = range(i*steps,(i+1)*steps)
    futures.append(pool.submit(m_run, partition))

done, not_done = wait(futures, timeout=3600, return_when=FIRST_COMPLETED)

# Shut down pool
pool.shutdown(wait=False)

# Kill remaining child processes
kill_child_processes(os.getpid())