使用asyncio做多项终极工作

Use asyncio to do multiple ultimate jobs

有2个工作:"wash_clothes"(job1)和"setup_cleaning_robot"(job2),每个工作需要你7秒3秒,你必须做到世界末日。

这是我的代码:

import asyncio


async def wash_clothes():
    print(f'Start job1')
    await asyncio.sleep(3)
    print(f'Finish job1, took 3 seconds')


async def setup_cleaning_robot():
    print(f'Start job2')
    await asyncio.sleep(7)
    print(f'Finish job2, took 7 seconds')


async def not_really_asyncio():
    kk = 1
    while True:
        job_list = [wash_clothes(), setup_robot()]
        await asyncio.gather(*job_list)
        kk += 1


async def main():
    await not_really_asyncio()
    # await really_asyncio()    # Still don't know how to do


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

这是输出

Start job1
Start job2
Finish job1, took 3 seconds
Finish job2, took 7 seconds
Start job1
Start job2
Finish job1, took 3 seconds
Finish job2, took 7 seconds
...
...

但我们知道我们可以放衣服,然后安装清洁机器人,休息一下(只有几秒钟),直到其中一项工作完成,然后现在就做,一次又一次.. .

正确的输出如下:

Start job1
Start job2
Finish job1, took 3 seconds
Start job1
Finish job1, took 3 seconds
Start job1
Finish job2, took 7 seconds
Start job2
Finish job1, took 3 seconds
...
...

目前我有使用线程的想法,但它会弄乱我的代码。

我希望代码尽可能简洁

最短的方法是:

# ...

async def constantly_wash_clothes():
    while True:
        await wash_clothes()


async def constantly_setup_cleaning_robot():
    while True:
        await setup_cleaning_robot()


async def main():
    await asyncio.gather(
        constantly_wash_clothes(),
        constantly_setup_cleaning_robot()
    )


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

它会给你想要的输出。

您想要但当前代码中没有发生的关键事情是,当洗完衣服后,您立即再次开始洗衣服,即使清洁机器人还在进度(并将再持续 4 秒)。但是这里的这一行阻止了:

async def not_really_asyncio():
    kk = 1
    while True:
        job_list = [wash_clothes(), setup_robot()]
        await asyncio.gather(*job_list)   # <--- Waits until *both* finish
        kk += 1

asyncio.gather 等待所有作业完成。所以即使 wash_clothes 已经完成,gather 也会继续坐在那里等待 setup_robot 也完成。

我可以想出两种方法来解决这个问题。

  1. while循环中,使用asyncio.wait()代替gather。这允许您等到只有一个任务完成,此时您可以立即启动它的另一个实例。

    这是修复您的代码当前所做的最直接的方法。然而,它非常棘手:asyncio.wait() 非常繁琐(它的参数是任务而不是协程,你必须分开它的 return 值)并且你的 while 循环将必须 运行 仅那些尚未 运行ning.

  2. 的任务
  3. 一个更横向的想法是为两个任务中的每一个使用单独的while循环。您可以将它们放在单独的函数中或直接放在 wash_clothes()setup_cleaning_robot() 函数中。

像这样:

counter = 0

async def keep_washing_clothes():
    while True:
        await wash_clothes()
        global counter
        counter += 1

async def keep_setting_up_cleaning_robot():
    while True:
        await setup_cleaning_robot()
        global counter
        counter += 1

async def really_asyncio():
    job_list = [keep_washing_clothes(), keep_setting_up_cleaning_robot()]
    await asyncio.gather(*job_list)

奖金聊天 如果将时间戳记放在 print() 输出中,您可能会发现更容易理解发生了什么:

# At the start of your script:
from datetime import datetime

# Later on:
print(datetime.utcnow().isoformat(), 'Finish job2, took 7 seconds')