asyncio.ensure future vs. BaseEventLoop.Utwórz zadanie vs. simple coroutine?

Widziałem kilka podstawowych samouczków Pythona 3.5 na asyncio wykonujących tę samą operację w różnych smakach. W tym kodzie:

import asyncio  

async def doit(i):
    print("Start %d" % i)
    await asyncio.sleep(3)
    print("End %d" % i)
    return i

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    #futures = [asyncio.ensure_future(doit(i), loop=loop) for i in range(10)]
    #futures = [loop.create_task(doit(i)) for i in range(10)]
    futures = [doit(i) for i in range(10)]
    result = loop.run_until_complete(asyncio.gather(*futures))
    print(result)

Wszystkie trzy powyższe warianty definiujące zmienną futures osiągają ten sam wynik; jedyną różnicą, jaką widzę, jest to, że w trzecim wariancie wykonanie jest nie w porządku (co w większości przypadków nie powinno mieć znaczenia). Czy jest jakaś inna różnica? Czy są przypadki, w których nie mogę po prostu użyć najprostszego wariantu (zwykła lista coroutines)?

Author: crusaderky, 2016-03-31

4 answers

ensure_future vs create_task

ensure_future jest metodą tworzenia Task od coroutine. Tworzy zadania na różne sposoby na podstawie argumentów (w tym używając create_task dla coroutines i obiektów future-like).

create_task jest abstrakcyjną metodą AbstractEventLoop. Różne pętle zdarzeń mogą implementować tę funkcję na różne sposoby.

Powinieneś użyć ensure_future do tworzenia zadań. Będziesz potrzebował create_task tylko wtedy, gdy zamierzasz zaimplementować własną pętlę zdarzeń Typ.

Upd:

@bj00 napisał (a):]}

Chodzi o to, że jeśli masz coś, co może albo koroutine albo Future (ta ostatnia zawiera Task, ponieważ jest to podklasa Future), i chcesz mieć możliwość wywołania metody na nim, który jest zdefiniowany tylko na Future (prawdopodobnie o jedynym użytecznym przykład: cancel()). Gdy jest już Future (lub Task) to nic nie robi; gdy jest a koroutine to owija to w Task.

Jeśli wiesz, że masz koroutine i chcesz, aby był zaplanowany, poprawnym API do użycia jest create_task(). Jedyny raz, kiedy powinieneś wywołanie ensure_future() jest wtedy, gdy dostarczasz API (jak większość asyncio ' s own APIs), który akceptuje albo koroutine albo Future i musisz zrobić z nim coś, co wymaga posiadania Future.

I Później:

W końcu nadal wierzę, że ensure_future() jest odpowiednio niejasna nazwa dla rzadko potrzebnego elementu funkcjonalności. Podczas tworzenia zadanie z koroutine należy użyć odpowiednio nazwanego loop.create_task(). Może powinien być na to pseudonim asyncio.create_task()?

To dla mnie zaskakujące. Moją główną motywacją do używania ensure_future przez cały czas było to, że jest to funkcja wyższego poziomu w porównaniu do członka pętli create_task (dyskusja zawiera niektóre pomysły, takie jak dodanie asyncio.spawn lub asyncio.create_task).

Mogę również wskazać, że w moim opinia dość wygodne jest używanie uniwersalnej funkcji, która może obsługiwać dowolne Awaitable, a nie tylko korutyny.

Jednak odpowiedź Guido jest jasna: " tworząc zadanie z koroutine powinieneś użyć odpowiednio nazwanego loop.create_task()"

Kiedy koroutiny powinny być owinięte w zadania?

Owiń koroutine w zadanie - jest to sposób na rozpoczęcie tego koroutine "w tle". Oto przykład:

import asyncio


async def msg(text):
    await asyncio.sleep(0.1)
    print(text)


async def long_operation():
    print('long_operation started')
    await asyncio.sleep(3)
    print('long_operation finished')


async def main():
    await msg('first')

    # Now you want to start long_operation, but you don't want to wait it finised:
    # long_operation should be started, but second msg should be printed immediately.
    # Create task to do so:
    task = asyncio.ensure_future(long_operation())

    await msg('second')

    # Now, when you want, you can await task finised:
    await task


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

Wyjście:

first
long_operation started
second
long_operation finished

Możesz zastąpić asyncio.ensure_future(long_operation()) po prostu poczuć różnicę.

 73
Author: Mikhail Gerasimov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-20 19:56:09

create_task()

  • zwraca zadanie,
  • jest wywoływany w kontekście pętli.

ensure_future()

  • akceptuje Futures, coroutines, awaitable objects,
  • zwraca zadanie (lub Future jeśli Future minęło).
  • jeśli dany arg jest koroutinem to używa create_task,
  • można przekazać obiekt pętli.

Jak widać create_task jest bardziej szczegółowy.


async Funkcja Bez create_task or ensure_future

Proste wywołanie async funkcja zwraca koroutine

>>> async def doit(i):
...     await asyncio.sleep(3)
...     return i
>>> doit(4)   
<coroutine object doit at 0x7f91e8e80ba0>

A ponieważ gather pod maską zapewnia (ensure_future), że args są futures, jawnie {[7] } jest zbędne.

Podobne pytanie Jaka jest różnica między pętlą.create_task, asyncio.async / ensure_future i zadanie?

 28
Author: kwarunek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-24 10:09:22

Dla przykładu, wszystkie trzy typy uruchamiają się asynchronicznie. jedyną różnicą jest to, że w trzecim przykładzie wstępnie wygenerowałeś wszystkie coroutiny 10 i przesłałeś je do pętli razem. więc tylko ostatni daje wyjście losowo.

 2
Author: ospider,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-18 10:52:17

Uwaga: ważna tylko dla Python 3.7 (dla Pythona 3.5 patrz wcześniejsza odpowiedź ).

Z oficjalnych dokumentów:

asyncio.create_task (dodany w Pythonie 3.7) jest preferowanym sposobem tworzenia nowych zadań zamiast ensure_future().


Szczegóły:

Więc teraz, w Pythonie 3.7, istnieją 2 funkcje owijania najwyższego poziomu (podobne, ale różne):

Cóż, utlimately obie te funkcje wrappera pomogą Ci wywołać BaseEventLoop.create_task. Jedyną różnicą jest ensure_future akceptować dowolne awaitable obiekt i pomóc przekształcić go w przyszłość. A także możesz podać swój własny parametr event_loop W ensure_future. I w zależności od tego, czy potrzebujesz tych możliwości, czy nie, możesz po prostu wybrać, której owijki użyć.

 2
Author: Yeo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-26 14:21:08