Python 3.x Asyncio在任务取消时阻止主事件循环

Python 3.x Asyncio在任务取消时阻止主事件循环,python-3.x,python-asyncio,blocking,event-loop,Python 3.x,Python Asyncio,Blocking,Event Loop,问题: async def __transform(self, request: web.Request): loop_example = LoopExample(request['parameters']) result = await loop_example.run_loop() return response class LoopExample: __loop = asyncio.get_event_loop()

问题:

 async def __transform(self, request: web.Request):
    loop_example = LoopExample(request['parameters'])
    result = await loop_example.run_loop()            
    return response

 class LoopExample:
     __loop = asyncio.get_event_loop()

     def __init__(self, parameters):
         self.parameters = user_context.logger

    async def run_loop(self):
        try:
           print(f"main threadId: {threading.get_ident()}")
           return await self.__loop.run_in_executor(None, self.wait)
        except Exception as e:
           self.logger.exception(e)
           print(f"exception threadid(goes back to main thread): {threading.get_ident()}")

    //the below method is the blocking call. 
    def wait(self):
        print(f"blocking loop threadId: {threading.get_ident()}")
        time.sleep(60)
Python版本:3.7

我们有一个API来调用数据库。 为了使DB调用不阻塞事件循环,我们在后台使用asyncio运行该任务。 我们收到大量请求,我们不希望在处理当前请求时阻止其他呼叫 传入请求的请求超时为60秒。 它在大多数情况下都能正常工作。但是当线程中运行的任务之一被取消时,主线程在几秒钟内没有响应

例如,下面是控制器中的一个函数。这将调用数据库。随着时间的推移,我把它简化了

代码:

 async def __transform(self, request: web.Request):
    loop_example = LoopExample(request['parameters'])
    result = await loop_example.run_loop()            
    return response

 class LoopExample:
     __loop = asyncio.get_event_loop()

     def __init__(self, parameters):
         self.parameters = user_context.logger

    async def run_loop(self):
        try:
           print(f"main threadId: {threading.get_ident()}")
           return await self.__loop.run_in_executor(None, self.wait)
        except Exception as e:
           self.logger.exception(e)
           print(f"exception threadid(goes back to main thread): {threading.get_ident()}")

    //the below method is the blocking call. 
    def wait(self):
        print(f"blocking loop threadId: {threading.get_ident()}")
        time.sleep(60)
注意事项:

 async def __transform(self, request: web.Request):
    loop_example = LoopExample(request['parameters'])
    result = await loop_example.run_loop()            
    return response

 class LoopExample:
     __loop = asyncio.get_event_loop()

     def __init__(self, parameters):
         self.parameters = user_context.logger

    async def run_loop(self):
        try:
           print(f"main threadId: {threading.get_ident()}")
           return await self.__loop.run_in_executor(None, self.wait)
        except Exception as e:
           self.logger.exception(e)
           print(f"exception threadid(goes back to main thread): {threading.get_ident()}")

    //the below method is the blocking call. 
    def wait(self):
        print(f"blocking loop threadId: {threading.get_ident()}")
        time.sleep(60)
我已经测试过同时发送多个请求。我可以看到多个线程ID(每个请求一个)被打印出来,显然它们没有阻塞主事件循环。但是当一个线程中运行的一个任务被取消时(因为当输入请求的HTTP超时时,我会得到如下CanceledError

“在运行\u循环中\n返回等待self.\uuu循环中.run\u执行器(无,self.wait)\nconcurrent.futures.\u base.canceledError”})”

发生这种情况时,服务在几秒钟内没有响应。几秒钟(~8秒)后,它会恢复正常。因此,在8秒钟内,到达端点的其他请求会得到502(网关超时错误)。 同样从文档中可以看出,python使用ThreadPoolExecutor作为默认值。 我尝试显式地指定ThreadPoolExecutor,这是相同的行为。有人能帮我找出这里的错误吗?
谢谢。

你能详细解释一下“但是当一个线程被取消时…”部分吗?这很令人困惑,因为线程不能被取消,异步IO任务可以。另外,关于“因为当输入请求的HTTP超时时,我得到一个CanceledError”“-超时是如何发生的?是外部事件、对等超时还是调用
asyncio.wait_for
或其他取消任务的asyncio API?@user4815162342这是一个在被取消的线程中运行的任务,是线程中运行的唯一任务(更新了上述问题)。对于第二个问题,是同伴超时。请求通过nginx发送,nginx中断了与服务器的连接。谢谢你的时间。这是一个在线程中运行的任务,被取消了-什么被取消了,线程还是任务?它是如何被取消的,你能显示取消它的代码吗?一个任务被取消了。我不知道如何显示启动取消的代码部分。发生这种情况时,我只会收到一个取消的错误。我认为取消任务的代码来自aiohttp。aiohttp中断请求,导致任务取消。好的,我想我现在明白了-您的代码正在等待
run\u in\u executor
,aiohttp由于超过超时期限而取消整个任务,您会得到
cancelederror
。但服务随后会停止几秒钟。我会尝试在整个过程中使用
strace-f-o tracefile
进行调试,并检查
tracefile
以了解在这8秒钟内该过程在做什么。