Используйте asyncio для выполнения нескольких основных задач - PullRequest
2 голосов
/ 12 февраля 2020

Есть 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
...
...

Пока у меня есть идея использовать многопоточность, но это испортит мой код.

Я хочу, чтобы код был максимально кратким

Ответы [ 2 ]

1 голос
/ 12 февраля 2020

Главное, чего вы хотите, чтобы этого не произошло в вашем текущем коде, это то, что, когда стирка белья заканчивается, вы немедленно начинаете стирку белья снова, даже если робот-уборщик все еще выполняется (и будет еще 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 до конца sh.

Я могу придумать два способа это исправить.

  1. В while l oop используйте asyncio.wait() вместо gather. Это позволяет вам подождать, пока только одна из задач не будет завершена, после чего вы можете сразу же запустить другой экземпляр.

    Это самый прямой способ исправить ваш код по сравнению с тем, что он делает в настоящее время. Тем не менее, это довольно сложно: asyncio.wait() довольно неудобно (его параметры являются задачами, а не сопрограммами, и вы должны выбрать его возвращаемое значение), и вашему while l oop придется выполнять только те задачи, которые не являются уже запущен.

  2. Еще одна побочная идея - использовать отдельный while l oop для каждой из двух задач. Вы можете поместить их в отдельные функции или напрямую в функции 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')
1 голос
/ 12 февраля 2020

Самый короткий путь будет:

# ...

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())

Это даст вам желаемый результат.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...