Python asyncio:是否可以取消由执行者运行的未来操作?

Python asyncio:是否可以取消由执行者运行的未来操作?,python,executor,event-loop,python-asyncio,Python,Executor,Event Loop,Python Asyncio,我想在一个执行器中使用asyncio调用loop.run\u in\u Executor启动一个阻塞函数,然后取消它,但这似乎对我不起作用 代码如下: import asyncio import time from concurrent.futures import ThreadPoolExecutor def blocking_func(seconds_to_block): for i in range(seconds_to_block): print('bloc

我想在一个执行器中使用asyncio调用loop.run\u in\u Executor启动一个阻塞函数,然后取消它,但这似乎对我不起作用

代码如下:

import asyncio
import time

from concurrent.futures import ThreadPoolExecutor


def blocking_func(seconds_to_block):
    for i in range(seconds_to_block):
        print('blocking {}/{}'.format(i, seconds_to_block))
        time.sleep(1)

    print('done blocking {}'.format(seconds_to_block))


@asyncio.coroutine
def non_blocking_func(seconds):
    for i in range(seconds):
        print('yielding {}/{}'.format(i, seconds))
        yield from asyncio.sleep(1)

    print('done non blocking {}'.format(seconds))


@asyncio.coroutine
def main():
    non_blocking_futures = [non_blocking_func(x) for x in range(1, 4)]
    blocking_future = loop.run_in_executor(None, blocking_func, 5)
    print('wait a few seconds!')
    yield from asyncio.sleep(1.5)

    blocking_future.cancel()
    yield from asyncio.wait(non_blocking_futures)



loop = asyncio.get_event_loop()
executor = ThreadPoolExecutor(max_workers=1)
loop.set_default_executor(executor)
asyncio.async(main())
loop.run_forever()
我希望上面的代码只允许阻塞函数输出:

blocking 0/5
blocking 1/5
然后查看非阻塞函数的输出。但事实上,即使我取消了,阻碍未来的事情仍在继续

可能吗?还有别的办法吗

谢谢


编辑:关于使用asyncio运行阻塞和非阻塞代码的更多讨论:

在这种情况下,一旦
未来
实际开始运行,就无法取消它,因为您依赖于
concurrent.futures.Future
的行为,并且:

取消()

尝试取消呼叫如果当前正在执行调用 如果无法取消,则该方法将返回
False
,否则 调用将被取消,方法将返回
True

因此,只有当任务在
执行器中仍处于挂起状态时,取消才会成功。现在,您实际使用的是一个
asyncio.Future
包在一个
并发的.futures.Future
上,实际上,
循环返回的
asyncio.Future
。run\u in\u executor()
在调用
cancel()
后,如果您试图
中屈服,则会引发一个
取消错误,即使基础任务实际上已经在运行。但是,它实际上不会取消
执行器中任务的执行

如果需要实际取消任务,则需要使用更常规的方法中断线程中运行的任务。如何做到这一点的细节取决于用例。对于您在示例中介绍的用例,您可以使用
线程化.Event

def blocking_func(seconds_to_block, event):
    for i in range(seconds_to_block):
        if event.is_set():
            return
        print('blocking {}/{}'.format(i, seconds_to_block))
        time.sleep(1)

    print('done blocking {}'.format(seconds_to_block))


...
event = threading.Event()
blocking_future = loop.run_in_executor(None, blocking_func, 5, event)
print('wait a few seconds!')
yield from asyncio.sleep(1.5)

blocking_future.cancel()  # Mark Future as cancelled
event.set() # Actually interrupt blocking_func

由于线程共享进程的相同内存地址空间,因此没有安全的方法终止正在运行的线程。这就是为什么大多数编程语言不允许杀死正在运行的线程的原因(围绕这一限制有很多丑陋的黑客)

Java是第一次学的

一个解决方案是在一个单独的进程中运行函数,而不是在一个线程中运行函数,并将其优雅地细化

库提供了一个类似于
concurrent.futures
的界面,支持运行要取消的
futures

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()  

谢谢你的回复。你建议的解决方案听起来很适合解决这个问题。我的问题是,在我的现实生活中,blocking_func在第三方。我将等待一两天,看看是否有人有任何进一步的建议;否则,我会将您的答案标记为已接受。您知道如何在完成时向阻塞函数添加回调吗?