【发布时间】: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