Python asyncio:是否可以取消由执行者运行的未来操作?
我想在一个执行器中使用asyncio调用loop.run\u in\u Executor启动一个阻塞函数,然后取消它,但这似乎对我不起作用 代码如下: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
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在第三方。我将等待一两天,看看是否有人有任何进一步的建议;否则,我会将您的答案标记为已接受。您知道如何在完成时向阻塞函数添加回调吗?