【问题标题】:Getting Result from Cancelled Task从已取消的任务中获取结果
【发布时间】:2019-08-28 22:49:58
【问题描述】:

我有一个程序,大致像下面的例子。 一个任务正在收集一些值并将它们返回给调用者。 有时任务可能会被取消。 在这些情况下,我仍然希望获得迄今为止任务收集的结果。 因此我捕获了CancelledError 异常,清理并返回完成的结果。

async def f():
    results = []
    for i in range(100):
        try:
            res = await slow_call()
            results.append(res)
        except asyncio.CancelledError:
            results.append('Undecided')
    return results

def on_done(task):
    if task.cancelled():
        print('Incomplete result', task.result()
    else:
        print(task.result())

async def run():
    task = asyncio.create_task(f())
    task.add_done_callback(on_done)

问题是任务取消后返回的值在任务中似乎不可用。 调用 task.result() 只会重新抛出 CancelledError。打电话给task._result 就是None

有没有办法获取已取消任务的返回值,假设它有一个?

编辑:我现在意识到捕获CancelledError 会导致任务根本没有被取消。 这给我留下了另一个难题:我如何向任务所有者发出信号,表明这个结果只是“一半”的结果,而任务真的被取消了。 我想我可以添加一个额外的返回值来表明这一点,但这似乎与任务取消系统的整个想法背道而驰。

这里有什么好的方法建议吗?

【问题讨论】:

  • 很确定 task 被取消也会导致未来被取消。这将始终导致未来引发CancelledError,与任务是否“失败”无关。也许任务可以通过将值推送到Queue 来“返回”值?
  • @SamMason 我确实考虑过对所有事情都使用队列。这似乎违背了任务的想法,首先要有result()s。
  • 这取决于您使用的 API。通常人们关心的任务是做一个定义明确的“任务”,但在这里你做的事情很多,部分结果是有用的。因此,在这种情况下,使用队列似乎更合适。请注意,您也可以将list 传递给f 并让它附加结果。您需要某种方式将此列表发送至on_done。队列可以让您在添加中间结果时获得中间结果,而不仅仅是等到任务“完成”,这完全取决于哪种抽象更合适!
  • 您为什么使用add_done_callback 而不仅仅是await 执行任务? asyncio 的最大优势之一是摆脱了以前存在的“回调地狱”,例如在扭曲的蟒蛇中。
  • @SamMason 这是因为我有很多任务。我试图使用asyncio.wait(),但我希望它返回要么 任务完成或抛出错误。目前看来,您必须选择等待哪个条件,所以我不得不使用on_done :(

标签: python python-asyncio


【解决方案1】:

我离理解用例还有很长的路要走,但以下内容对我来说是明智的:

import asyncio

async def fn(results):
    for i in range(10):
        # your slow_call
        await asyncio.sleep(0.1)
        results.append(i)

def on_done(task, results):
    if task.cancelled():
        print('incomplete', results)
    else:
        print('complete', results)

async def run():
    results = []
    task = asyncio.create_task(fn(results))
    task.add_done_callback(lambda t: on_done(t, results))
    # give fn some time to finish, reducing this will cause the task to be cancelled
    # you'll see the incomplete message if this is < 1.1
    await asyncio.sleep(1.1)

asyncio.run(run())

run 中使用add_done_callbacksleep 感觉很尴尬,让我觉得我不明白你在做什么。也许将包含更多调用代码的内容发布到https://codereview.stackexchange.com 将有助于获得更好的构建事物的方法的想法。请注意,与 asyncio 内置库(IMO 过早标准化)相比,还有其他库(如 trio)为 Python 协程提供更好的接口

【讨论】:

    【解决方案2】:

    我不认为这是可能的,因为在我看来,与取消任务的含义相冲突。 您可以在 slow_call 中实现类似的行为,方法是触发 CancelledError,在函数中捕获它,然后返回您想要的任何内容。

    【讨论】:

    • 好吧,我抓住它并返回我想要的任何东西。问题是我返回的值被扔掉了。
    猜你喜欢
    • 1970-01-01
    • 2013-12-27
    • 2018-04-15
    • 2016-10-15
    • 1970-01-01
    • 2022-08-17
    • 2017-04-03
    • 2018-02-24
    • 2012-02-10
    相关资源
    最近更新 更多