Python 确保仅当等待的协同路由被取消时,包装协同路由才被取消
我需要包装一个返回数据的协同程序。如果返回了数据,则该数据不再可用。如果取消协同路由,则数据可在下次调用时使用。我需要包装协同程序具有相同的行为,但是有时它会在包装协同程序已经完成时被取消 我可以用下面的代码重现这种行为Python 确保仅当等待的协同路由被取消时,包装协同路由才被取消,python,python-3.x,python-asyncio,Python,Python 3.x,Python Asyncio,我需要包装一个返回数据的协同程序。如果返回了数据,则该数据不再可用。如果取消协同路由,则数据可在下次调用时使用。我需要包装协同程序具有相同的行为,但是有时它会在包装协同程序已经完成时被取消 我可以用下面的代码重现这种行为 导入异步IO loop=asyncio.get\u event\u loop() fut=asyncio.Future() 异步def wait(): 返回等待未来 task=asyncio.确保未来(wait()) 异步定义测试(): 等待异步睡眠(0.1) 未来设置结果(“
导入异步IO
loop=asyncio.get\u event\u loop()
fut=asyncio.Future()
异步def wait():
返回等待未来
task=asyncio.确保未来(wait())
异步定义测试():
等待异步睡眠(0.1)
未来设置结果(“数据”)
打印('fut',fut)
打印('任务',任务)
task.cancel()
等待异步睡眠(0.1)
打印('fut',fut)
打印('任务',任务)
循环。运行\u直到完成(test())
输出清楚地表明,包装协同路由在协同路由完成后被取消,这意味着数据将永远丢失。我无法屏蔽这两个呼叫,因为如果我被取消,我没有任何数据可以返回
fut <Future finished result='data'>
task <Task pending coro=<wait() running at <ipython-input-8-6d115ded09c6>:7> wait_for=<Future finished result='data'>>
fut <Future finished result='data'>
task <Task cancelled coro=<wait() done, defined at <ipython-input-8-6d115ded09c6>:6>>
任务
数据[]
数据已使用,但任务已取消,因此无法检索数据。直接等待
get_data()
会起作用,但不能取消。我认为您需要首先保护等待的未来不被取消,然后检测您自己的取消。如果未来尚未完成,则将取消传播到其中(有效地撤消屏蔽()
)并退出。如果未来已完成,则忽略取消并返回数据
代码如下所示,也进行了更改,以避免使用全局变量,并使用asyncio.run()
(如果使用的是Python 3.6,则可以转到run\u直到完成()
请注意,忽略取消请求可能被视为滥用取消机制。但是,如果知道任务会在之后继续(理想情况下是立即完成),那么在您的情况下,它可能是正确的。小心。这是我没有考虑的事情。对于用户来说,取消一个未完成的任务,仍然需要等待并检查任务是否最终有任何结果,这是相当违反直觉的。但在内部使用时,这是一种有效的方法。
task <Task cancelled coro=<wrapper_data() done, defined at <ipython-input-2-93645b78e9f7>:16>>
data []
import asyncio
async def wait(fut):
try:
return await asyncio.shield(fut)
except asyncio.CancelledError:
if fut.done():
# we've been canceled, but we have the data - ignore the
# cancel request
return fut.result()
# otherwise, propagate the cancellation into the future
fut.cancel()
# ...and to the caller
raise
async def test():
loop = asyncio.get_event_loop()
fut = loop.create_future()
task = asyncio.create_task(wait(fut))
await asyncio.sleep(0.1)
fut.set_result('data')
print ('fut', fut)
print ('task', task)
task.cancel()
await asyncio.sleep(0.1)
print ('fut', fut)
print ('task', task)
asyncio.run(test())