1. Concurrency vs. Parallelism
To understand asynchronous programming, we must distinguish between concurrency and parallelism:-
Concurrency: Handling multiple tasks during overlapping periods of time. Tasks do not necessarily execute at the same time. Concurrency means multiple tasks are in progress.
-
Parallelism: Executing multiple tasks at the exact same time, usually on different CPU cores (e.g., rendering video, matrix multiplication).
2. I/O-Bound vs. CPU-Bound Tasks
Understanding the nature of your workload helps determine the best concurrency model.I/O-Bound Tasks
The program spends most of its time waiting for external operations to complete.- Examples: API requests, database queries, network requests, disk file operations.
- Solution:
- If an asynchronous library is available: Asyncio
- If only synchronous/blocking libraries are available: Multithreading
CPU-Bound Tasks
The program spends most of its time calculating and performing computations on the CPU.- Examples: Large mathematical calculations, image/video processing, machine learning inference, cryptographic operations.
- Solution: Multiprocessing
3. Concurrency Models in Python
Python provides three primary ways to handle concurrent execution: Asyncio, Multithreading, and Multiprocessing.A. Asyncio (Asynchronous I/O)
asyncio provides concurrency using an event loop, typically running in a single thread. When a task waits for I/O, it pauses and yields control back to the event loop.
- Best for: Async API calls, async database queries, WebSockets, network operations, and handling many concurrent I/O operations.
Note: Use asyncio when the I/O library supports asynchronous operations.
B. Multithreading
Multithreading uses multiple threads within a single process. Because of Python’s Global Interpreter Lock (GIL), only one thread can execute Python bytecodes at a time, making threads ideal for waiting on blocking network or file calls rather than CPU calculations.-
start()→ Starts the thread’s activity. -
join()→ Blocks the calling thread until the thread whosejoin()method is called terminates. - Best for: Blocking API calls, synchronous libraries, blocking file/network operations, and legacy code without async support.
Note: Use threads when the operation is I/O-bound but the library is synchronous/blocking.
C. Multiprocessing
Multiprocessing creates separate processes, each with its own Python interpreter and memory space. This completely bypasses the GIL, allowing true parallel execution across multiple CPU cores.- Best for: Heavy calculations, image/video processing, CPU-intensive data manipulation, and computational algorithms.
Note: Use multiprocessing when the work requires significant CPU computation.
4. Async & Await Declarations
Python’sasyncio framework uses the keywords async def and await to write asynchronous code.
Coroutines
Declaring a function withasync def creates a coroutine. Calling a coroutine does not run it; it returns a coroutine object. To execute it, you must await it.
5. The Event Loop & Task Scheduling
The Event Loop is the engine that runs asynchronous applications. It manages the execution of different tasks:- It runs a task until the task hits an
awaitexpression (blocking I/O). - While that task waits for I/O (e.g., waiting for database results), the loop pauses it and switches to run another ready task.
- Once the I/O operation finishes, the event loop resumes the original task.
Running Tasks Concurrently
To run multiple operations concurrently instead of sequentially, you can group them intoasyncio.create_task() or use asyncio.gather().
6. Why FastAPI Uses Async
FastAPI is built on ASGI (Asynchronous Server Gateway Interface) and supports nativeasync def route handlers.
When a client sends a request to an async def endpoint that performs a database query or external API fetch, FastAPI yields control back to the event loop. The event loop can process other incoming requests in the meantime, resulting in massive throughput gains.
7. Quick Decision Guide
Use this flowchart and table to quickly decide which concurrency model fits your task:Final Summary
- Asyncio → Asynchronous I/O
- Multithreading → Blocking/Synchronous I/O
- Multiprocessing → CPU-bound work
- Concurrency = Multiple tasks are in progress (overlapping execution).
- Parallelism = Multiple tasks execute simultaneously (requires multiple CPU cores).
Key Takeaway: Parallelism is a form of concurrency, but concurrency does not necessarily mean parallelism.
Practice & Exercises
To reinforce what you’ve learned in this section (async/await declarations, tasks, event loops, and concurrency), practice with these interactive notebooks:Follow-Along Practice
Practice defining async coroutines, working with non-blocking sleeps, understanding task scheduling, and implementing concurrent operations using asyncio.gather.💻 VS Code | 🚀 Colab | 📥 Download
Practice Exercises
Test your knowledge with hands-on exercises including simulated async file downloaders and concurrent batch file managers.💻 VS Code | 🚀 Colab | 📥 Download