取消任务后,Asyncio锁就会死锁

时间:2019-12-06 08:48:22

标签: python aiohttp python-asyncio python-socketio

我最近使用python-socketio和aiohttp编写了一个客户端/服务器应用程序,我的应用程序基于异步名称空间(服务器端),此外,我的on_message事件中有许多等待调用,因此必须使用异步锁以确保我保持所需的流量。为了实现此行为,我编写了一个装饰器,并用它包装了每个关键部分类型的函数。

@async_synchronized('_async_mutex')
    async def on_connect(self, sid, environ):
        self._logger.info("client with sid: {} connected to namespace: {}!".format(
            sid, __class__.__name__))
        important_member = 1
        await other_class.cool_coroutine()
        important_member = 2

在我的构造函数中,我已经初始化_async_mutex = asyncio.Lock()

装饰器:

def async_synchronized(tlockname):
    """A decorator to place an instance based lock around a method """

    def _synched(func):
        @wraps(func)
        async def _synchronizer(self, *args, **kwargs):
            tlock = self.__getattribute__(tlockname)
            try:
                async with tlock:
                    return await func(self, *args, **kwargs)
            finally:
                pass
        return _synchronizer

    return _synched

现在,在任何正常使用的情况下,一切都可以正常工作(关闭/打开客户端会正确触发功能,并且锁将按预期执行)。 请务必注意,我的on_disconnect函数使用完全相同的装饰器和锁包装。 我遇到的问题是,当客户端的网络适配器物理断开连接时(正常的客户端关闭工作正常),我看到确实调用了on_disconnect事件,但另一个协同例程当前持有该锁。由于某种原因,该事件被多次触发,并最终陷入僵局。

我已经用装饰物包装了我的装饰器,这些印刷品描述了锁的状态/调用功能,并且还在每个异步调用周围添加了try / catch。 似乎我所有的例程都捕获到一个已取消的异常(由aiohttp假定),因此有一种方法“取消”了该锁定,并且该锁定从未被释放。我尝试用asyncio.shield()包装每个异步调用,但是行为没有改变。

我应该在这里采用另一种异步锁方法吗? (完全删除锁可以解决问题,但可能导致应用程序的计算部分出现不确定的行为)

更多代码示例: 实际的on_connect和on_disconnect事件:

    @async_synchronized('_async_mutex')
    async def on_connect(self, sid, environ):
        self._logger.info("very good log message")
        self._connected_clients_count += 1

    @async_synchronized('_async_mutex')
    async def on_disconnect(self, sid):
        self._logger.info("very good disconnect message")
        self._connected_clients_count -= 1
        await self._another_namespace_class.inform_client_disconnect(sid) # this method is wrapped with the same decorator but with a different lock

注意:另一个没有连接相同的客户端。另外,当发生网络断开时,我也看不到日志消息(我将日志级别设置为调试)

0 个答案:

没有答案
相关问题