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)?
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 zawieraTask
, ponieważ jest to podklasaFuture
), i chcesz mieć możliwość wywołania metody na nim, który jest zdefiniowany tylko naFuture
(prawdopodobnie o jedynym użytecznym przykład:cancel()
). Gdy jest jużFuture
(lubTask
) to nic nie robi; gdy jest a koroutine to owija to wTask
.Jeśli wiesz, że masz koroutine i chcesz, aby był zaplanowany, poprawnym API do użycia jest
create_task()
. Jedyny raz, kiedy powinieneś wywołanieensure_future()
jest wtedy, gdy dostarczasz API (jak większość asyncio ' s own APIs), który akceptuje albo koroutine alboFuture
i musisz zrobić z nim coś, co wymaga posiadaniaFuture
.
I Później:
To dla mnie zaskakujące. Moją główną motywacją do używaniaW 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 nazwanegoloop.create_task()
. Może powinien być na to pseudonimasyncio.create_task()
?
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ę.
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?
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.
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ń zamiastensure_future()
.
Szczegóły:
Więc teraz, w Pythonie 3.7, istnieją 2 funkcje owijania najwyższego poziomu (podobne, ale różne):
-
asyncio.create_task
: które po prostu callevent_loop.create_task(coro)
bezpośrednio. (zobacz kod źródłowy ) -
ensure_future
które również wywołująevent_loop.create_task(coro)
jeśli jest to coroutine lub po prostu należy upewnić się, że typ zwracany jest asyncio .Future . (patrz kod źródłowy ). W każdym razie, {[6] } jest nadalFuture
ze względu na dziedziczenie klas (ref ).
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ć.
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