Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 确保仅当等待的协同路由被取消时,包装协同路由才被取消_Python_Python 3.x_Python Asyncio - Fatal编程技术网

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