python asyncio.create_task 任务比预期提前退出?

python asyncio.create_task tasks exiting earlier than expected?

我有以下代码:

import asyncio

async def myfunc(i):
    print("hello", i)
    await asyncio.sleep(i)
    print("world", i)

async def main():
    asyncio.create_task(myfunc(2))
    asyncio.create_task(myfunc(1))
    
asyncio.run(main())

它输出:

hello 2
hello 1

请注意,world 并未在任何地方打印。为什么会产生我们看到的输出?我期待:

hello 2
hello 1
world 1
world 2

因为我认为 asyncio.sleep(i) 调用会将执行交给事件循环,此时事件循环会在它们各自的等待时间后重新安排它们。显然我误会了。有人可以解释一下吗?

问题是 main 中的循环没有等待任务完成导致任务没有完成执行。使用 asyncio.gather() 启动并等待所有协程执行。

import asyncio


async def myfunc(i):
    print("hello", i)
    await asyncio.sleep(i)
    print("world", i)


async def main():
    await asyncio.gather(myfunc(2), myfunc(1))


asyncio.run(main())

你在评论中描述的逻辑比较复杂,没有实现它的功能,所以你必须设计逻辑,在这种情况下,必须满足2个条件才能完成应用程序:

  • 必须启动所有任务。
  • 应该没有活动任务。

考虑到这一点,我使用 Future 创建一个标志来指示它,还使用 ​​add_done_callback 通知我作业已完成。

import asyncio
from collections import deque
import random


async def f(identifier, seconds):
    print(f"Starting task: {identifier}, seconds: {seconds}s")
    await asyncio.sleep(seconds)
    print(f"Finished task: {identifier}")
    return identifier


T = 3


class Manager:
    def __init__(self, q):
        self._q = q
        self._future = asyncio.get_event_loop().create_future()
        self._active_tasks = set()
        self._status = False

    @property
    def q(self):
        return self._q

    def launch_task(self):
        try:
            identifier = self.q.pop()
        except IndexError:
            return False
        else:
            seconds = random.uniform(T - 1, T + 1)
            task = asyncio.create_task(f(identifier, seconds))
            self._active_tasks.add(identifier)
            task.add_done_callback(self._finished_callback)
            return True

    async def work(self):
        self._status = True
        while self.launch_task():
            await asyncio.sleep(T)
        self._status = False
        await self._future

    def _finished_callback(self, c):
        self._active_tasks.remove(c.result())
        if not self._active_tasks and not self._status:
            self._future.set_result(None)


async def main():
    identifiers = deque(range(10))
    manager = Manager(identifiers)
    await manager.work()


if __name__ == "__main__":
    asyncio.run(main())

找到了比@eyllanesc 提供的解决方案简单得多的解决方案。原来有一个实现它的函数