Python Asyncio任务正在运行,但没有gather()

2024-04-29 05:27:00 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图重现并更好地理解cristiangarcia在this博客文章中的TaskPool示例,结果得到了一个非常有趣的结果。在

这是我使用的两个脚本。我用一个随机的睡眠呼叫交换了一个实际的网络请求

#task_pool.py
import asyncio

class TaskPool(object):

    def __init__(self, workers):
        self._semaphore = asyncio.Semaphore(workers)
        self._tasks = set()

    async def put(self, coro):
        await self._semaphore.acquire()
        task = asyncio.create_task(coro)
        self._tasks.add(task)
        task.add_done_callback(self._on_task_done)

    def _on_task_done(self, task):
        self._tasks.remove(task)
        self._semaphore.release()

    async def join(self):
        await asyncio.gather(*self._tasks)

    async def __aenter__(self):
        return self

    def __aexit__(self, exc_type, exc, tb):
        print("aexit triggered")
        return self.join()

以及

^{pr2}$

python3.7.1上的命令main.py 10产生以下结果。在

request: 0 start, delay: 3
request: 1 start, delay: 3
request: 2 start, delay: 3
request: 0 end
request: 1 end
request: 2 end
request: 3 start, delay: 4
request: 4 start, delay: 1
request: 5 start, delay: 0
request: 5 end
request: 6 start, delay: 1
request: 4 end
request: 6 end
request: 7 start, delay: 1
request: 8 start, delay: 4
request: 7 end
aexit triggered
request: 9 start, delay: 1
request: 9 end
request: 3 end
request: 8 end

基于这个结果,我有几个问题。在

  1. 在上下文管理器退出并触发__aexit__之前,我不会期望任务运行,因为这是asyncio.gather的唯一触发器。然而print语句强烈暗示fetch工作甚至在{}之前就已经发生了。到底发生了什么事?任务正在运行吗?如果是,是什么引起的?在
  2. 与(1)有关。为什么上下文管理器在所有作业都返回之前退出?在
  3. fetch作业应该返回一个元组。如何访问此值?对于基于web的应用程序,我想开发人员可能希望对网站返回的数据进行操作。在

非常感谢任何帮助!在


Tags: pyselfasynciotaskasyncrequestdefstart
1条回答
网友
1楼 · 发布于 2024-04-29 05:27:00
  1. 调用^{}后,任务立即启动。在

    直接从文档开始,第一行:

    Wrap the coro coroutine into a Task and schedule its execution.

  2. 不应该,但是。看看你问题中的代码:

    def __aexit__(self, exc_type, exc, tb):
        print("aexit triggered")
        return self.join()
    

    有三个问题:

    • 这是一个常规的同步函数。将其更改为async def,并添加必需的await,以调用self.join()。在这里,您不调用join,您只需创建任务而不运行它。你的python肯定会抱怨你从不等待任务。这些警告绝对不能被忽略,因为它们意味着程序中出现了严重错误。在

      [edit:正如下面user4815162342所指出的,您编写的构造将实际起作用,尽管可能不是出于预期的原因-它起作用的原因是调用self.join()而返回的协同例程函数将被返回并使用,就像它是aexit自己的函数一样。你不想这样,让它异步并等待。

    • 一旦解决了这个问题,__aexit__将打印“aexit触发器”,然后然后调用join,等待任务完成。因此,来自尚未完成任务的消息将出现在“aexit触发”消息之后。

    • 将忽略__aexit__的返回值,除非由于引发异常而导致退出。在这种情况下,return True将吞并异常。删除return

    所以那个部分,修复了:

    async def __aexit__(self, exc_type, exc, tb):
        print("aexit triggered")
        await self.join()
        print("aexit completed")
    
  3. 您的TaskPool必须使任务的结果可用。这是你的设计,python不会在引擎盖下做任何魔术。根据您所掌握的,一个简单的方法是joingather的结果存储为任务池的一个属性。

相关问题 更多 >