import asyncio
import time


async def func():
    print('task start')
    await asyncio.sleep(10)
    print('task end')


async def main():
    task1 = asyncio.create_task(func())
    task2 = asyncio.create_task(func())
    task3 = asyncio.create_task(func())
    task4 = asyncio.create_task(func())
    s = time.monotonic()
    print('main start', time.monotonic() - s)
    await task1
    print('main continue', time.monotonic() - s)
    await task2
    print('main continue', time.monotonic() - s)
    await task3
    print('main continue', time.monotonic() - s)
    await task4
    print('main end', time.monotonic() - s)


asyncio.run(main())

Ten kod daje wyniki jak poniżej:

main start 0.0
task start
task start
task start
task start
task end
task end
task end
task end
main continue 10.0
main continue 10.0
main continue 10.0
main end

Ale jak to możliwe, Python ominął moje poprzednie wywołania print, najpierw uruchomił awaitables, a następnie wrócił do wywołań print, jak mam to zrozumieć?

2
iouvxz 11 marzec 2020, 14:47

2 odpowiedzi

Najlepsza odpowiedź

Wszystkie Twoje zadania śpią przez 10 sekund, a następnie kontynuuj i zakończ niemal natychmiast. Zatem wszystkie wywołania await zostaną odblokowane w tym samym czasie, ponieważ po zakończeniu zadania 1 wszystkie zadania również zostaną zakończone.

Masz rację w tym sensie, że technicznie mogłeś przeplatać wydruki między task end a main continue, ale myślę, że jest to szczegół implementacji, który wydaje się być zgrupowany.

Myślę, że możesz lepiej zrozumieć, co się dzieje, korzystając z tego zmodyfikowanego skryptu:

import asyncio
import time


async def func(task_nb, wait):
    print('[%s] task start' % task_nb)
    await asyncio.sleep(wait)
    print('[%s] task end' % task_nb)


async def main():
    task1 = asyncio.create_task(func(1, 1))
    task2 = asyncio.create_task(func(2, 5))
    task3 = asyncio.create_task(func(3, 7))
    task4 = asyncio.create_task(func(4, 2))
    s = time.monotonic()
    print('main start', time.monotonic() - s)
    await task1
    print('main continue', time.monotonic() - s)
    await task2
    print('main continue', time.monotonic() - s)
    await task3
    print('main continue', time.monotonic() - s)
    await task4
    print('main end', time.monotonic() - s)


asyncio.run(main())

Będziesz mieć bardziej interesujące zachowanie await:

main start 1.81000359589234e-07
[1] task start
[2] task start
[3] task start
[4] task start
[1] task end
main continue 1.0019499360005284
[4] task end
[2] task end
main continue 5.001785704000213
[3] task end
main continue 7.003587035000237
main end 7.003632674000073
1
aveuiller 11 marzec 2020, 12:02

Twój kod robi to, co powinien, zgodnie ze specyfikacją asyncio, ale być może nie rozumiesz, czym jest „zadanie”.

Z dokumentów:

asyncio.create_task(coro, *, name=None)

Owiń coro coroutine w zadanie i zaplanuj jego wykonanie. Zwróć obiekt Task.

Oznacza to, że ponieważ utworzyłeś 4 zadania na początku swojego głównego zadania, wszystkie są zaplanowane do wykonania w tym samym czasie. I tak wszystkie drukują task start razem, a potem task end razem, w którym to momencie wszystkie Twoje await natychmiast wypadają, ponieważ wszystkie zadania są zakończone w tym samym czasie (10 sekund później ). W końcu widzisz main continue 10.0 3 razy.

Wypróbuj ten kod, uważam, że będzie on zachowywał się tak, jak tego oczekujesz.

async def main():
    task1 = func()
    task2 = func()
    task3 = func()
    task4 = func()
    ...
0
Hymns For Disco 11 marzec 2020, 12:01