Asynchronous programming in Python, using the asyncio
library, enables you to write concurrent code that efficiently handles tasks with potentially long waiting times, such as I/O operations. It utilizes asynchronous functions (coroutines) and an event loop to manage the execution flow. Here’s a guide to getting started with asyncio
:
1. Coroutines and async
/await
:
1.1 Defining Coroutines:
Coroutines are functions declared with async def
. They can be paused using await
and allow other tasks to run in the meantime.
import asyncio
async def my_coroutine():
print("Coroutine is running")
await asyncio.sleep(2)
print("Coroutine is done")
# Running the coroutine
asyncio.run(my_coroutine())
1.2 await
for Asynchronous Operations:
Use await
to pause the execution of the coroutine until an asynchronous operation, like I/O or a timer, is completed.
async def example():
print("Start")
await asyncio.sleep(1)
print("After 1 second")
asyncio.run(example())
2. Event Loop:
The event loop is the central component managing the execution of coroutines. It schedules and runs tasks, handles I/O operations, and ensures concurrency.
import asyncio
async def my_coroutine():
print("Coroutine is running")
await asyncio.sleep(2)
print("Coroutine is done")
# Create an event loop and run the coroutine
loop = asyncio.get_event_loop()
loop.run_until_complete(my_coroutine())
loop.close()
3. Tasks and asyncio.gather()
:
3.1 Creating Tasks:
Tasks are units of work in asyncio
. They allow you to concurrently run multiple coroutines.
import asyncio
async def task1():
await asyncio.sleep(2)
print("Task 1 is done")
async def task2():
await asyncio.sleep(1)
print("Task 2 is done")
# Create tasks and run them concurrently
task1 = asyncio.create_task(task1())
task2 = asyncio.create_task(task2())
asyncio.run(task1)
asyncio.run(task2)
3.2 asyncio.gather()
:
Use asyncio.gather()
to run multiple coroutines concurrently and wait for all of them to complete.
import asyncio
async def task1():
await asyncio.sleep(2)
print("Task 1 is done")
async def task2():
await asyncio.sleep(1)
print("Task 2 is done")
# Run tasks concurrently using asyncio.gather()
async def main():
await asyncio.gather(task1(), task2())
asyncio.run(main())
4. Asynchronous I/O:
asyncio
excels in handling asynchronous I/O operations. Use functions like asyncio.open()
for file I/O or aiohttp
for asynchronous HTTP requests.
import asyncio
async def read_file(file_path):
async with asyncio.open(file_path, 'r') as file:
content = await file.read()
print(content)
asyncio.run(read_file('example.txt'))
5. Concurrency with asyncio.Lock()
:
To ensure that certain sections of code are executed by only one coroutine at a time, use asyncio.Lock()
.
import asyncio
async def my_coroutine(lock):
async with lock:
print("Locked section")
await asyncio.sleep(2)
print("Unlocked section")
async def main():
lock = asyncio.Lock()
await asyncio.gather(my_coroutine(lock), my_coroutine(lock))
asyncio.run(main())
6. Exception Handling:
Handle exceptions within coroutines or tasks using try
and except
. Unhandled exceptions can be caught using asyncio.gather()
.
import asyncio
async def my_coroutine():
try:
print("Coroutine is running")
await asyncio.sleep(2)
raise ValueError("Something went wrong")
except ValueError as e:
print(f"Caught an exception: {e}")
asyncio.run(my_coroutine())
7. Conclusion:
Asynchronous programming with asyncio
in Python provides a powerful way to write concurrent code that efficiently handles tasks with potential waiting times. By using coroutines, the event loop, and various asyncio
features, developers can design responsive and scalable applications, particularly in scenarios involving I/O-bound operations. Understanding the principles of asynchronous programming and how to apply them using asyncio
is valuable for building high-performance Python applications.