Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does asyncio.create_task() do?

What does asyncio.create_task() do? I have looked at the docs and can't seem to understand it. A bit of code that confuses me is this:

import asyncio  async def counter_loop(x, n):     for i in range(1, n + 1):         print(f"Counter {x}: {i}")         await asyncio.sleep(0.5)     return f"Finished {x} in {n}"  async def main():     slow_task = asyncio.create_task(counter_loop("Slow", 4))     fast_coro = counter_loop("Fast", 2)      print("Awaiting Fast")     fast_val = await fast_coro     print("Finished Fast")      print("Awaiting Slow")     slow_val = await slow_task     print("Finished Slow")      print(f"{fast_val}, {slow_val}")  asyncio.run(main()) 

This gives the following output:

001 | Awaiting Fast 002 | Counter Fast: 1 003 | Counter Slow: 1 004 | Counter Fast: 2 005 | Counter Slow: 2 006 | Finished Fast 007 | Awaiting Slow 008 | Counter Slow: 3 009 | Counter Slow: 4 010 | Finished Slow 011 | Finished Fast in 2, Finished Slow in 4 

I don't understand quite how this is working.

  1. Shouldn't the slow_task not be able to run until the completion of the fast_coro because it was never used in an asyncio.gather method?
  2. Why do we have to await slow_task?
  3. Why is Awaiting Slow printed after the coroutine appears to have started?
  4. What really is a task? I know that what gather is doing is scheduling a task. And create_task supposedly creates a task.

An in-depth answer would be greatly appreciated. Thanks!

It also might be worth mentioning that I know very little about Futures.

like image 902
BeastCoder Avatar asked Jun 23 '20 06:06

BeastCoder


People also ask

What is Asyncio Create_task?

It's an asyncio construct that tracks execution of a coroutine in a concrete event loop. When you call create_task , you submit a coroutine for execution and receive back a handle. You can await this handle when you actually need the result, or you can never await it, if you don't care about the result.

What are tasks in Asyncio?

Tasks within Asyncio are responsible for the execution of coroutines within an event loop. These tasks can only run in one event loop at one time and in order to achieve parallel execution you would have to run multiple event loops over multiple threads.

What is the point of Asyncio?

asyncio is a library to write concurrent code using the async/await syntax. asyncio is used as a foundation for multiple Python asynchronous frameworks that provide high-performance network and web-servers, database connection libraries, distributed task queues, etc.


1 Answers

What does asyncio.create_task() do?

It submits the coroutine to run "in the background", i.e. concurrently with the current task and all other tasks, switching between them at await points. It returns an awaitable handle called a "task" which you can also use to cancel the execution of the coroutine.

It's one of the central primitives of asyncio, the asyncio equivalent of starting a thread. (In the same analogy, awaiting the task with await is the equivalent of joining a thread.)

Shouldn't the slow_task not be able to run until the completion of the fast_coro

No, because you explicitly used create_task to start slow_task in the background. Had you written something like:

    slow_coro = counter_loop("Slow", 4)     fast_coro = counter_loop("Fast", 2)     fast_val = await fast_coro 

...indeed slow_coro would not run because no one would have yet submitted it to the event loop. But create_task does exactly that: submit it to the event loop for execution concurrently with other tasks, the point of switching being any await.

because it was never used in an asyncio.gather method?

asyncio.gather is not the only way to achieve concurrency in asyncio. It's just a utility function that makes it easier to wait for a number of coroutines to all complete, and submit them to the event loop at the same time. create_task does just the submitting, it should have probably been called start_coroutine or something like that.

Why do we have to await slow_task?

We don't have to, it just serves to wait for both coroutines to finish cleanly. The code could have also awaited asyncio.sleep() or something like that. Returning from main() (and the event loop) immediately with some tasks still pending would have worked as well, but it would have printed a warning message indicating a possible bug. Awaiting (or canceling) the task before stopping the event loop is just cleaner.

What really is a task?

It's an asyncio construct that tracks execution of a coroutine in a concrete event loop. When you call create_task, you submit a coroutine for execution and receive back a handle. You can await this handle when you actually need the result, or you can never await it, if you don't care about the result. This handle is the task, and it inherits from Future, which makes it awaitable and also provides the lower-level callback-based interface, such as add_done_callback.

like image 90
user4815162342 Avatar answered Sep 25 '22 01:09

user4815162342