Async IO Python

Learn via video course
FREE
View all courses
Python Course for Beginners With Certification: Mastering the Essentials
Python Course for Beginners With Certification: Mastering the Essentials
by Rahul Janghu
1000
4.90
Start Learning
Python Course for Beginners With Certification: Mastering the Essentials
Python Course for Beginners With Certification: Mastering the Essentials
by Rahul Janghu
1000
4.90
Start Learning
Topics Covered

Overview

In this article, we shall learn about asyncio Python. Python has been around and used since it was developed by Guido van Rossum (1991). One of its features is its code readability i.e. executable pseudocode. Python is a hybrid language supporting both object-oriented and functional programming. It is also an interactive programming language. Although it is used as a scripting language, it is also used for full-scale development.

Asynchronous Programming in Python

In Python, there are two worlds, sync and async. You can see them as two different worlds of Python as they have different methods of working, like different ways of calling functions, but they share syntax and variables. We cannot directly use the synchronous database from the async and asynchronous database from sync but both can be achieved by running the code by explicitly crossing into the world.

In the synchronous world, we call the function, and everything written on-screen gets processed. By using threads, we can run the code in parallel in the same process, it is the only built-in option. But in an asynchronous world, things work differently around, we can run several coroutines because here everything works on a central event loop. Coroutines tend to work until they strike await and they stop for a moment and call into the event loop.

Now this signifies, that we can't just mix around async with sync. If we try to await a Python function, Python will complain, and on the other hand, if we forget to await an async function, Python will give back a coroutine object rather than the result that was desired.

You don’t need to know the in-detail working, just remember, coroutines have to give up control explicitly through an await.

Now let us take an example, suppose there is a cook who has to prepare some breakfast i.e. toast some bread, boil some eggs, and maybe make some tea. Now to complete this task there are two different methods: sync and async.

Synchronus performed by one thread: So now, we start toasting the bread, Waiting until the bread gets toasted. We remove the bread. Now boil the water, again waiting till the water gets boiled. Insert eggs into boiling water. Wait till the eggs get boiled, and then take the eggs out. Again boils the waters to make tea.

We can see that we have to wait a lot and at that moment the thread could do other processes.

Async-await again one thread: We start toasting the bread and on the other hand boil the water for eggs and tea. Wait until one of the processes gets finished and then start doing the other task. Say that the water gets boiled and then we put the eggs in boiling water and again wait for another task to finish.

Now in this method, only one person is working i.e. one thread is involved. Now because of this, the code looks synchronous but there is not much need to make the variable thread-safe. It is easier to see that the breakfast will be ready in a shorter time i.e. the bread will still be warm till the eggs get cooked up.

Real asynchronous several threads: Now while making the breakfast we can save time by hiring more cooks to make breakfast while you prepare the tea. This is the most costly as we have to involve more threads.

Difference Between Parallelism, Concurrency, Threading, and Async IO

This article is based only on async IO, however, it will be worth spending some minutes studying its counterparts.

Parallelism*is the task of performing multiple operations at the same time. Multiprocessing is a way that affects parallelism and it involves spreading tasks all over the central processing unit (CPU). Multiprocessing is well tasked with CPU-bound tasks.

Concurrency on the other hand is wider than what Parallelism suggests. It conveys that multiple tasks can work in a superimposed manner. It means that the execution of more than one tasks can be interleaved. For example in a multiprocessing OS, multiple processes may execute concurrently.

Threading , is a concurrent model, where turn after turn multiple threads finish a task. Multiple threads may be involved in one process. Thanks to GIL, Python has a very complex connection with threading. The important thing to know about threading is that it is better for IO-bound tasks. An IO-bound job is in much waiting on input/output to complete.

Now it's time for Python asyncio package to come. Python documentation billed async IO as a library to write concurrent code. It is built on top of multithreading. Async IO is a singly threaded, single-process design.

The Asyncio Package and Async/Await

Python asyncio is a library that uses async/await syntax to write concurrent code. Multiple Python asynchronous frameworks that provide web servers and high-performance networks, distributed task queues, database connection libraries, etc use asyncio as the foundation. For IO-bound and high-level structured network code, asyncio is often a perfect fit. asyncio comes with many high-level APIs to control subprocessors, run Python coroutines concurrently, etc. Python’s asyncio package which was introduced in Python 3.4 has two keywords async and await, which both have some different purposes, but when together they help to build an asynchronous Python program.

At the very core of python async IO lies coroutine. It is a somewhat different version of the Python function generator. A coroutine is a function that before its execution or before reaching return can suspense and indirectly give control to other coroutines for some time.

Now let us write some async IO code to help you understand more about Python asyncio.

Output

Now just notice the difference, if we were to define the functions with def and time.sleep().

The order of this output is the core of the async IO. When every single task reaches await.sleep(1), the function calls to the event loop, saying that “I am going to sleep for 2 seconds, go and do something useful in the meantime”.

Now let us look at the synchronous version.

Now after the execution we can notice that there is a slight but critical change in order and execution time.

Output

Components of Async IO Programming

  • Coroutine: A coroutine (i.e. cooperative subroutine), a function for preventive multitasking, dynamically surrenders to other processes and routines, rather than just being forcefully seized by the kernel. Coroutine was termed by Melvin Conway in 1958.

In asyncio, this discretionary preemption is called awaiting. In Python, a function prefixed with async keyword is the coroutine function. For example, the following code block is the definition of a coroutine function that prints 'Hi':

  • Tasks: An asyncio task is an object that covers a coroutine and gives methods to manipulate its execution and question its status. One of the things to notice about the tasks in asycnio is that, we do not directly create them, we just use ensure_future() or the AbstractEventLoop.create_task() method. Let us have a quick look.

Output

  • Event Loops: In asyncio, the scheduling and communication of awaitable objects are controlled by the event loop. To use awaitables event loop is required. Every asyncio at least contains one event loop. There can also be multiple loops. There are multiple ways to run an event loop.

We can make use of run_forever(), which will run until the stop() function is called. Also, we can use the run_until_complete() method which will run until the given parameter has completed its execution. Here we are going to take an example of run_until_complete() method.

Output

Now in the above example, we will define a coroutine (here we defined it as eveLoop), which will be passed to the run_until_complete() function as a parameter. We will observe that the run_until_complete() will run until eveLoop() has complete its execution.

  • Future: A future returns a value, not now but later in the future. The return value of the future is the result of the asynchronous operation. For example, when we call an API from a remote server, we except that we will get the result later.

When the future is used with the await keyword, it pauses the future till it returns a value. The code below shows the use of await keyword in the future.

Output

Important Tasks for Synchronization in Python

Introdution

If multiple threads are executing on a data or object at the same time, then there may be some chances of data inconsistency. Let us understand it through an example.

There is an online bus ticket service and there are only three tickets left, and two users are trying to book them simultaneously, only one will get the ticket and the other will get fail. In this example, the problem arises, when two threads process the same data. To prevent that, code synchronization should be implemented which will restrict the threads to work on the same object or data simultaneously.

Queues

When we design an application, which processes events or data, we need to store the data and then need to distribute this data to a set of workers. These workers can do anything depending upon the events concurrently. asyncio provides an asynchronous queue, which helps to do this task. We can add segments of data and run workers concurrently pulling and accessing the data and processing it.

Check out this article to know more about Queue in Python.

Synchronization Primitives

Although asyncio apps run as a single-threaded process, they are still built as concurrent apps. Every coroutine or task may execute differently in an unforeseeable order, based on delays and interrupts of IO and external events. To support safe concurrency, asyncio provides the same low-level primitives found in threading and multiprocessing modules.

When should we use Asynchronous Programming?

Asynchronous programming is always not good to use. It adds up more complexity to the program and also makes it unreadable to the programmers. Young and new programmers more often use the async function very much because they believe that it will act as a safeguard that their code will work at the run time.

General rules for when to use asynchronous programming:

  • Good for: Tasks that may take a while; high iteration.
  • Bad for: Simplicity (New programmers can not read it).

Important Sunctions in Asyncio Python

Running a asyncio program

*Code

Output

Explanation

  • First, we import the asyncio module to use its functionality.
  • Now create a function named primary(), and add the keyword "async" to make the function run asynchronously.
  • We will use for loop and call sleep() function in python which will force us to wait for 1 second.
  • After the wait, the program will print "Hello".
  • Program also should contain one .run() function and one .main() function.

Creating Tasks

Code

Output

Explanation

  • First, start a timer.
  • Now, create a text and make a schedule to run on the event loop immediately.
  • Also, create another variable and again make a schedule to run on the event loop immediately.
  • Now, wait till the tasks get completed.
  • Now using await keyword is important to wait for the tasks to complete at some time.
  • Now finally show the time it takes to run the main() function.

As we use the create_task() function, the program is much faster, and also the more task you run the faster it becomes.

Sleeping

Code

Output

Explanation

  • Sleep suspends the current tasks and allows other tasks to run.
  • When setting the delay to 0, will provide an optimized path to other tasks to run. This can be achieved by running long functions to prevent blocking the event loop for the full duration of the function call.

Timeouts

Code

Output

Explanation

  • First, we will define a call_api() coroutine, which will take 3 seconds to get completed by default.
  • Now we will create a task that will wrap the coroutine and will take 5 seconds to get completed.
  • Now we will use the asyncio.wait_for() function to wait for seconds to get completed. Now, since the tasks will take 5 seconds to complete and a timeout will happen so a timeout error will be raised.

Conclusion

  • Python has been around and used since it was developed by Guido van Rossum (1991).
  • Python is a hybrid language supporting both object-oriented and functional programming.
  • In Python, there are two worlds, sync and async. You can see them as two different worlds of Python as they have different methods of working, like different ways of calling functions, but they share syntax and variables.
  • In the synchronous world, we call the function, and everything written on-screen gets processed. By using threads, we can run the code in parallel in the same process, it is the only built-in option.
  • In an asynchronous world, things work differently around, we can run several coroutines because here everything works on a central event loop. Coroutines tend to work until they strike await and they stop for a moment and call into the event loop.
  • Parallelism is the task of performing multiple operations at the same time. Multiprocessing is a way that affects parallelism and it involves spreading tasks all over the central processing unit (CPU). Multiprocessing is well tasked with CPU-bound tasks.
  • Concurrency on the other hand is wider than what Parallelism suggests. It conveys that multiple tasks can work in a superimposed manner.
  • Threading , is a concurrent model, where turn after turn multiple threads finish a task. Multiple threads may be involved in one process. Thanks to GIL, Python has a very complex connection with threading. The important thing to know about threading is that it better for IO-bound tasks. An IO-bound job is in much waiting on input/output to complete.
  • Python documentation billed async IO as a library to write concurrent code. It is built on multithreading, neither on the concurrency nor on both of them. Async IO is a singly threaded, single-process design. It is concurrent like programming.
  • Python documentation billed async IO as a library to write concurrent code. It is built on multithreading, neither on the concurrency nor on both of them. Async IO is a singly threaded, single-process design. It is concurrent like programming.
  • A coroutine is a function that before its execution or before reaching return can suspense and indirectly give control to other coroutines for some time.
  • In asyncio, the scheduling and communication of awaitable objects are controlled by the event loop.
  • An asyncio task is an object that covers a coroutine and gives methods to manipulate its execution and question its status. One of the things to notice about the tasks in asycnio is that, we do not directly create them, we just use ensure_future() or the AbstractEventLoop.create_task() method.
  • A future returns a value, not now but later in the future. The return value of the future is the result of the asynchronous operation. For example, when we call an API from a remote server, we 1except that we will get the result later.
  • When we design an application, which processes events or data, we need to store the data and then need to distribute this data to a set of workers. These workers can do anything depending upon the events concurrently. asyncio provides an asynchronous queue, which helps to do this task.
  • To support safe concurrency, asyncio provides the same low-level primitives found in threading and multiprocessing modules.

See Also: