带回调的asyncio.future会在取消后导致异常

时间:2018-03-31 15:51:12

标签: python python-asyncio

在正常关闭时取消它们时,我遇到了几个预定的asyncio.futures(被安排为服务器的消息编写器)的问题。 每个活动客户端连接都有一个活动编写器会话。

writer = asyncio.gather(
   *[self._dispatchMessage(message, c) for c in connections],
   loop=self.loop,
   return_exceptions=True
   )

每个作家未来(GatheringFuture)都存储在活动会话列表中......

connection.sessions = set()

...作家未来一旦完成就会被删除。这是通过添加" done_callback"走向未来。

writer.add_done_callback(lambda task: connection.sessions.remove(task))
connection.sessions.add(writer)

一旦我优雅地关闭了我的服务器,我就会遍历活动连接及其会话并取消(future.cancel())它们不会以未来的作家未来结束。

async def cancel_sessions(connection):
    for s in connection.sessions:
    s.cancel()

asyncio.wait(
    [await cancel_sessions(c) for c in client_connections],
    timeout=None
    )

这样可行,但是只要我有更多的连接,我就会不断获得asyncio循环异常,尽管几乎无处不在地调试异常以进行调试。对我来说,看起来,问题是由" done_callbacks"添加到作家期货(GatheringFuture)。似乎我必须删除call_backs,否则我最终得到:

Traceback (most recent call last):
  File "uvloop/cbhandles.pyx", line 49, in uvloop.loop.Handle._run
  File "...server.py", line 353, in <lambda>
    writer.add_done_callback(lambda task: connection.sessions.remove(task))
myUtilsAsyncLoopException: Async exception "<_GatheringFuture finished result=[None, None]>"

如果我从代码中省略这一行......

writer.add_done_callback(lambda task: connection.sessions.remove(task))

......我没有遇到问题。我现在的问题是如何处理取消的asyncio.futures上的done_callbacks。我的印象是,在取消之前,我不必手动删除未来的回调。但似乎在用回调取消未来时,这些回调可能会在相关未来被取消时抛出异常。

我还不明白为什么只有当我有超过2个连接时才会发生这种情况,因为我认为处理它们没有太大区别。

2 个答案:

答案 0 :(得分:0)

我想我自己解决了。通常有助于在问题中制定问题以更好地理解情况。我的问题似乎与asyncio无关,而是一个简单的逻辑问题。所有connection.sessions都有相同的(编写者)未来,实际上asyncio.future“done_callbacks”仍然在取消时被调用,但是将相同的future(writer)添加到几个connection.sessions会导致lambda回调中的一个关键错误。

答案 1 :(得分:0)

对于阅读这个问题的每个人:问题源于注册&#34;完成回调&#34;每个可用连接的未来。这发生在for循环中,但回调从未在未来完成时绑定到实际连接,而是绑定到for循环的最后一个连接。因此总是相同的连接对象。

文档建议使用functools将某些值绑定到未来的回调。这最终起作用(将for循环中的值绑定到回调中)并且没有&#34;有趣&#34;副作用。