DEV Community

Deepika Pusala
Deepika Pusala

Posted on

Week-05 ,Task- 1: Asynchronous Python - asyncio Deep Dive

Concurrency can sound scary when you first hear words like Thread, Process, GIL, Coroutine, Event Loop, Future, Asyncio, etc.

But don't worry!

At the basic level, all of these concepts are about one simple problem:

How can a program handle multiple things efficiently instead of doing everything one by one?

Let's understand everything from scratch.


1. What is a Thread? ๐Ÿงต

A thread is a small path of execution inside a program.

Normally, a Python program does things one after another:

Task 1
  โ†“
Task 2
  โ†“
Task 3
  โ†“
Task 4
Enter fullscreen mode Exit fullscreen mode

A thread allows another path of execution to exist inside the same program.

Think of a thread as a worker.

Program
โ”‚
โ”œโ”€โ”€ Thread 1
โ”œโ”€โ”€ Thread 2
โ””โ”€โ”€ Thread 3
Enter fullscreen mode Exit fullscreen mode

Each thread can work on a different task.

Simple Example

import threading

def say_hello():
    print("Hello!")

thread = threading.Thread(target=say_hello)

thread.start()
Enter fullscreen mode Exit fullscreen mode

Important

  • Thread(...) creates a thread.
  • start() starts the thread.
  • Threads are useful when a program has multiple things to handle.
  • Threads are especially useful for I/O-bound work, such as network requests and file operations.

2. What is Multithreading? ๐Ÿงต

Multithreading means using multiple threads inside one program.

For example:

Program
โ”‚
โ”œโ”€โ”€ Thread 1 โ†’ Download a file
โ”œโ”€โ”€ Thread 2 โ†’ Call an API
โ”œโ”€โ”€ Thread 3 โ†’ Read a file
โ””โ”€โ”€ Thread 4 โ†’ Query a database
Enter fullscreen mode Exit fullscreen mode

Instead of waiting for one task to completely finish before starting another, multiple tasks can make progress.

Real-World Example

Imagine downloading 4 files.

Without multithreading:

Download File 1
      โ†“
Download File 2
      โ†“
Download File 3
      โ†“
Download File 4
Enter fullscreen mode Exit fullscreen mode

With multithreading:

Thread 1 โ†’ File 1
Thread 2 โ†’ File 2
Thread 3 โ†’ File 3
Thread 4 โ†’ File 4
Enter fullscreen mode Exit fullscreen mode

This can be much more efficient when the tasks spend a lot of time waiting.


3. What is a Process? ๐Ÿญ

A process is basically a running program.

For example, when you run:

python app.py
Enter fullscreen mode Exit fullscreen mode

your operating system starts a process for that program.

A process has its own:

  • Memory
  • Resources
  • Python interpreter
  • Threads

A process can contain multiple threads:

Process
โ”‚
โ”œโ”€โ”€ Thread 1
โ”œโ”€โ”€ Thread 2
โ””โ”€โ”€ Thread 3
Enter fullscreen mode Exit fullscreen mode

The important thing to remember is:

A process is a running program, while a thread is a path of execution inside that process.


4. What is Multiprocessing? ๐Ÿญ

Multiprocessing means using multiple processes.

For example:

Process 1 โ†’ CPU work
Process 2 โ†’ CPU work
Process 3 โ†’ CPU work
Process 4 โ†’ CPU work
Enter fullscreen mode Exit fullscreen mode

Each process has its own memory space.

Python Example

from multiprocessing import Process

def work():
    print("Doing some work")

process = Process(target=work)

process.start()
process.join()
Enter fullscreen mode Exit fullscreen mode

Here:

  • Process() creates a new process.
  • start() starts it.
  • join() waits for it to finish.

Multiprocessing is especially useful for CPU-bound tasks.


5. Thread vs Task โš”๏ธ

These two words are easy to confuse.

They are not the same thing.

Task

A task is simply a piece of work that needs to be done.

For example:

Task:
"Download a profile picture"
Enter fullscreen mode Exit fullscreen mode

Thread

A thread is a way of executing work.

Think about it like this:

Task = WHAT needs to be done

Thread = WHERE the work can run

For example:

Task โ†’ Download an image
        โ†“
Thread โ†’ executes the work
Enter fullscreen mode Exit fullscreen mode

In asyncio, tasks work differently. An asyncio task is used to schedule a coroutine to run on the event loop.


6. Multithreading vs Multiprocessing โš”๏ธ

This is one of the most important differences.

Multithreading Multiprocessing
Uses multiple threads Uses multiple processes
Threads belong to one process Each process is separate
Threads share process memory Processes have separate memory
Lightweight More resource-heavy
Great for many I/O tasks Great for CPU-heavy tasks
Affected by the GIL for Python CPU work Can use multiple CPU cores

Simple rule

If your program is mostly waiting:

๐ŸŒ Network
๐Ÿ“ Files
๐Ÿ—„๏ธ Database
Enter fullscreen mode Exit fullscreen mode

Think about:

Threading
asyncio
Enter fullscreen mode Exit fullscreen mode

If your program is mostly calculating:

๐Ÿงฎ Heavy calculations
๐Ÿ–ผ๏ธ Image processing
๐ŸŽฅ Video processing
Enter fullscreen mode Exit fullscreen mode

Think about:

Multiprocessing
Enter fullscreen mode Exit fullscreen mode

7. What is a Lock? ๐Ÿ”’

A lock is used when multiple threads are accessing shared data.

Imagine we have:

balance = 100
Enter fullscreen mode Exit fullscreen mode

Two threads try to change it at the same time.

Thread 1 โ†’ withdraw money
Thread 2 โ†’ withdraw money
Enter fullscreen mode Exit fullscreen mode

Both might try to access the balance at the same time.

This can cause incorrect results.

A lock makes sure that only one thread enters a particular section of code at a time.

Example

import threading

lock = threading.Lock()

with lock:
    balance -= 50
Enter fullscreen mode Exit fullscreen mode

Think of a lock like a bathroom key:

๐Ÿ”’ Someone is inside
       โ†“
Everyone else waits
       โ†“
๐Ÿ”“ Person comes out
       โ†“
Next person enters
Enter fullscreen mode Exit fullscreen mode

So:

A lock protects shared data from being changed by multiple threads at the same time.


8. What is the GIL? ๐Ÿ˜ˆ

GIL stands for:

Global Interpreter Lock

The GIL is a feature of the standard Python implementation, CPython.

The important beginner-level idea is:

The GIL allows only one thread at a time to execute Python bytecode within a CPython process.

This becomes important when we talk about CPU-heavy work.

Imagine:

Thread 1 โ†’ Python code
Thread 2 โ†’ Python code
Thread 3 โ†’ Python code
Thread 4 โ†’ Python code
Enter fullscreen mode Exit fullscreen mode

You might expect all four threads to execute Python code on four CPU cores at exactly the same time.

The GIL prevents that kind of true parallel execution of Python bytecode within one CPython process.

But this does not mean threads are useless!

Threads are still very useful for I/O-bound tasks because programs often spend a lot of time waiting for:

  • Network responses
  • Database responses
  • Files
  • External services

9. Why is Threading Still Useful? โœ…

Suppose a program sends a network request:

Send request
     โ†“
WAIT...
     โ†“
Server responds
Enter fullscreen mode Exit fullscreen mode

During that waiting period, the CPU doesn't need to continuously execute Python code for that request.

Another thread can make progress.

For example:

Thread 1 โ†’ API request โ†’ WAITING
Thread 2 โ†’ process another request
Thread 3 โ†’ read a file
Enter fullscreen mode Exit fullscreen mode

So even with the GIL:

Threading can be very useful for I/O-bound programs.


10. What is Concurrency? โšก

Concurrency means handling multiple tasks during overlapping periods of time.

It does not necessarily mean that everything is running at the exact same instant.

For example:

Task A โ†’ โ–ˆโ–ˆโ–ˆ WAIT โ–ˆโ–ˆโ–ˆ
Task B โ†’     โ–ˆโ–ˆโ–ˆ WAIT โ–ˆโ–ˆโ–ˆ
Task C โ†’          โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
Enter fullscreen mode Exit fullscreen mode

The program switches between tasks and makes progress whenever a task is ready.

A simple way to remember it:

Concurrency = dealing with multiple tasks at once.


11. Why Do We Need Concurrency? ๐Ÿค”

Because programs spend a lot of time waiting.

Imagine:

API request     โ†’ 3 seconds
Database query  โ†’ 2 seconds
File operation  โ†’ 4 seconds
Enter fullscreen mode Exit fullscreen mode

If we do everything one by one:

3 + 2 + 4 = 9 seconds
Enter fullscreen mode Exit fullscreen mode

But if the waiting periods overlap:

API request     โ†’ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
Database query  โ†’   โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
File operation  โ†’    โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ
Enter fullscreen mode Exit fullscreen mode

The total time can be much smaller.

This is why concurrency is useful.


12. Concurrency vs Parallelism ๐Ÿš€

These words sound similar, but they are different.

Concurrency

Multiple tasks are being handled during overlapping periods.

Task A โ†’ โ–ˆโ–ˆโ–ˆ WAIT โ–ˆโ–ˆโ–ˆ
Task B โ†’    โ–ˆโ–ˆโ–ˆ WAIT โ–ˆโ–ˆโ–ˆ
Enter fullscreen mode Exit fullscreen mode

One worker can manage multiple tasks.

Parallelism

Multiple tasks are actually running at the same time.

CPU Core 1 โ†’ Task A
CPU Core 2 โ†’ Task B
CPU Core 3 โ†’ Task C
Enter fullscreen mode Exit fullscreen mode

Easy Example

Concurrency

One person:

Cook ๐Ÿณ
โ†“
While food cooks, wash dishes
โ†“
Check food
โ†“
Prepare salad
Enter fullscreen mode Exit fullscreen mode

Parallelism

Two people:

Person 1 โ†’ Cooking
Person 2 โ†’ Washing dishes
Enter fullscreen mode Exit fullscreen mode

They are doing things at the same time.


13. I/O-Bound vs CPU-Bound Tasks ๐ŸŒ๐Ÿงฎ

This is extremely important because it helps us decide which concurrency approach to use.


I/O-Bound Tasks

I/O means Input/Output.

Examples:

  • Network requests
  • API calls
  • Database queries
  • Reading files
  • Writing files
  • Downloading files

These tasks spend a lot of time waiting.

Example:

response = requests.get(url)
Enter fullscreen mode Exit fullscreen mode

The program may spend time waiting for the server.

Think:

I/O-bound = "I'm mostly waiting."


CPU-Bound Tasks

CPU-bound tasks spend most of their time doing calculations.

Examples:

  • Heavy mathematical calculations
  • Image processing
  • Video processing
  • Compression
  • Large loops
  • Some machine-learning workloads

Example:

total = 0

for i in range(1_000_000_000):
    total += i
Enter fullscreen mode Exit fullscreen mode

Here the CPU is doing a lot of work.

Think:

CPU-bound = "I'm mostly calculating."


14. Why Does Threading Exist? ๐Ÿงต

Computers often have to wait.

For example:

Send API request
       โ†“
     WAIT
       โ†“
Response arrives
Enter fullscreen mode Exit fullscreen mode

Instead of leaving the program completely idle, another thread can do something useful.

For example:

Thread 1 โ†’ API request โ†’ waiting
Thread 2 โ†’ Process another request
Thread 3 โ†’ Read a file
Enter fullscreen mode Exit fullscreen mode

Therefore, threading is particularly useful for programs that perform lots of I/O operations.


15. What Does the GIL Change? ๐Ÿ˜ˆ

Without understanding the GIL, you might think:

"If I create 4 threads, my CPU-heavy Python program will automatically use 4 CPU cores."

In CPython, that's not how it works.

The GIL means that only one thread at a time executes Python bytecode within a process.

Therefore:

CPU-heavy Python work
        +
Multiple threads
        โ†“
Not true CPU parallelism
Enter fullscreen mode Exit fullscreen mode

This is one reason multiprocessing is useful for CPU-heavy Python work.


16. Why is Multiprocessing Needed? ๐Ÿญ

Multiprocessing allows us to use multiple processes.

For example:

CPU Core 1 โ†’ Process 1
CPU Core 2 โ†’ Process 2
CPU Core 3 โ†’ Process 3
CPU Core 4 โ†’ Process 4
Enter fullscreen mode Exit fullscreen mode

Each process is separate.

This allows CPU-heavy work to run in parallel across CPU cores.

So:

Multiprocessing is especially useful when the CPU is the bottleneck.


17. Why Does asyncio Exist? ๐ŸŒŠ

You might ask:

"If threading already exists, why do we need asyncio?"

Good question!

Imagine a server handling thousands of network connections.

Creating a separate thread for every connection can become expensive.

asyncio provides another approach.

Instead of having many threads:

Thread 1 โ†’ waiting
Thread 2 โ†’ waiting
Thread 3 โ†’ waiting
Thread 4 โ†’ waiting
...
Enter fullscreen mode Exit fullscreen mode

we can use an event loop:

One thread
    โ†“
Event Loop
    โ†“
Task 1 โ†’ waiting
Task 2 โ†’ waiting
Task 3 โ†’ waiting
Task 4 โ†’ waiting
Enter fullscreen mode Exit fullscreen mode

When one task is waiting, the event loop can work on another task.

This makes asyncio especially useful for applications with many I/O operations.


18. What is an Event Loop? ๐Ÿ”„

The event loop is the heart of asyncio.

It manages asynchronous tasks.

Think of it as a manager:

                 Event Loop
                     โ”‚
       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
       โ†“             โ†“             โ†“
    Task A         Task B        Task C
    waiting         ready        waiting
Enter fullscreen mode Exit fullscreen mode

The event loop checks:

"Which task is ready to continue?"

If Task A is waiting for a network response:

Task A โ†’ WAITING
Enter fullscreen mode Exit fullscreen mode

the event loop can work on Task B.

Later, when Task A is ready:

Task A โ†’ READY
Enter fullscreen mode Exit fullscreen mode

the event loop can continue it.


19. What is a Coroutine? ๐Ÿงฉ

A coroutine is a special function that can pause and resume.

A normal function usually runs from start to finish:

def hello():
    print("Hello")
    print("World")
Enter fullscreen mode Exit fullscreen mode

An async function can pause while waiting:

async def hello():
    print("Hello")

    await asyncio.sleep(2)

    print("World")
Enter fullscreen mode Exit fullscreen mode

Here the coroutine can pause at:

await asyncio.sleep(2)
Enter fullscreen mode Exit fullscreen mode

and allow another task to run.

Later, it can continue.

Think:

Coroutine
   โ†“
Start
   โ†“
Do some work
   โ†“
WAIT
   โ†“
Pause
   โ†“
Continue later
   โ†“
Finish
Enter fullscreen mode Exit fullscreen mode

20. async and await โŒจ๏ธ

These are the two most important keywords in Python's async programming.

async

async is used to define an asynchronous function.

async def fetch_data():
    print("Fetching data...")
Enter fullscreen mode Exit fullscreen mode

This function is a coroutine function.


await

await tells Python:

"Wait for this asynchronous operation, but allow other async tasks to run while we're waiting."

Example:

import asyncio

async def fetch_data():
    print("Start")

    await asyncio.sleep(2)

    print("Finished")
Enter fullscreen mode Exit fullscreen mode

The important part is:

await asyncio.sleep(2)
Enter fullscreen mode Exit fullscreen mode

The coroutine waits, but the event loop can work on other tasks.


21. What is an Asyncio Task? ๐Ÿ“‹

An asyncio task is used to schedule a coroutine to run.

Example:

task = asyncio.create_task(fetch_data())
Enter fullscreen mode Exit fullscreen mode

Think about the difference:

Coroutine
    โ†“
The work to be performed

Task
    โ†“
The scheduled work
Enter fullscreen mode Exit fullscreen mode

For example:

async def download():
    ...
Enter fullscreen mode Exit fullscreen mode

This defines the coroutine.

Then:

task = asyncio.create_task(download())
Enter fullscreen mode Exit fullscreen mode

schedules it as a task.


22. What is a Future? ๐Ÿ”ฎ

A Future represents a result that will be available later.

Think about ordering food ๐Ÿ•

Order placed
     โ†“
Food is not ready
     โ†“
WAIT...
     โ†“
Food ready
     โ†“
Get result
Enter fullscreen mode Exit fullscreen mode

A Future is similar.

It represents something that:

Pending
   โ†“
Waiting
   โ†“
Completed
   โ†“
Result available
Enter fullscreen mode Exit fullscreen mode

You don't usually need to create Futures manually when you're just starting with asyncio.

They are more important when understanding how asynchronous systems work internally.


23. asyncio.gather() ๐Ÿค

asyncio.gather() is used when you want to run multiple asynchronous operations and collect their results.

Example:

import asyncio

async def task1():
    await asyncio.sleep(2)
    return "Task 1 done"

async def task2():
    await asyncio.sleep(1)
    return "Task 2 done"

async def main():
    results = await asyncio.gather(
        task1(),
        task2()
    )

    print(results)

asyncio.run(main())
Enter fullscreen mode Exit fullscreen mode

The two tasks can make progress concurrently.

The results are collected together.

Conceptually:

             gather()
                โ”‚
       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
       โ†“        โ†“        โ†“
    Task 1   Task 2   Task 3
       โ†“        โ†“        โ†“
    Result   Result   Result
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                โ†“
             Results
Enter fullscreen mode Exit fullscreen mode

Use gather() when:

"I have several async operations and I want their results together."


24. asyncio.wait() โณ

asyncio.wait() gives you more control over tasks.

Example:

done, pending = await asyncio.wait(tasks)
Enter fullscreen mode Exit fullscreen mode

You get two groups:

done
 โ†“
Tasks that finished

pending
 โ†“
Tasks that are still running
Enter fullscreen mode Exit fullscreen mode

Conceptually:

Tasks
 โ”‚
 โ”œโ”€โ”€ Task 1 โ†’ DONE
 โ”œโ”€โ”€ Task 2 โ†’ DONE
 โ”œโ”€โ”€ Task 3 โ†’ PENDING
 โ””โ”€โ”€ Task 4 โ†’ PENDING
Enter fullscreen mode Exit fullscreen mode

So:

done
Enter fullscreen mode Exit fullscreen mode

contains completed tasks.

And:

pending
Enter fullscreen mode Exit fullscreen mode

contains unfinished tasks.


25. asyncio.wait_for() โฐ

asyncio.wait_for() is used when you want to put a time limit on an async operation.

Example:

result = await asyncio.wait_for(
    fetch_data(),
    timeout=5
)
Enter fullscreen mode Exit fullscreen mode

This means:

"Wait for fetch_data(), but don't wait longer than 5 seconds."

If the operation takes too long, it is cancelled and a timeout error occurs.

This is very useful for network requests.

You don't want:

API request
    โ†“
WAIT...
    โ†“
WAIT...
    โ†“
WAIT FOREVER ๐Ÿ˜ญ
Enter fullscreen mode Exit fullscreen mode

Instead:

API request
    โ†“
Wait up to 5 seconds
    โ†“
Done or timeout
Enter fullscreen mode Exit fullscreen mode

26. Cancellation โŒ

Sometimes we start a task but no longer need it.

We can cancel it.

Example:

task = asyncio.create_task(do_work())

task.cancel()
Enter fullscreen mode Exit fullscreen mode

Cancellation basically means:

"Stop this task because we don't need it anymore."

This can happen when:

  • A user cancels an operation
  • A request times out
  • The application is shutting down
  • Another task makes the current task unnecessary

Conceptually:

Task
 โ†“
Running
 โ†“
Cancel requested
 โ†“
Cancelled
Enter fullscreen mode Exit fullscreen mode

27. Cooperative Concurrency ๐Ÿค

Cooperative concurrency means that tasks cooperate by giving control back when they are waiting.

This is an important idea in asyncio.

For example:

async def task():
    print("Start")

    await asyncio.sleep(2)

    print("Continue")
Enter fullscreen mode Exit fullscreen mode

When the task reaches:

await asyncio.sleep(2)
Enter fullscreen mode Exit fullscreen mode

it effectively says:

"I'm waiting. You can work on something else."

Then another task can run.

Conceptually:

Task A
  โ†“
await
  โ†“
Pause ๐Ÿคš
  โ†“
Task B runs
  โ†“
Task C runs
  โ†“
Task A becomes ready
  โ†“
Task A continues
Enter fullscreen mode Exit fullscreen mode

This is called cooperative concurrency because tasks give up control at appropriate points.


28. How Everything Connects ๐Ÿ”—

Now let's connect all the concepts.

                    CONCURRENCY
                         โ”‚
          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
          โ†“              โ†“              โ†“
      Threading     Multiprocessing   asyncio
          โ”‚              โ”‚              โ”‚
          โ†“              โ†“              โ†“
       Threads       Processes      Event Loop
          โ”‚              โ”‚              โ”‚
          โ†“              โ†“              โ†“
     Good for I/O    Good for CPU    Good for I/O
                                         โ”‚
                                         โ†“
                                    Coroutines
                                         โ”‚
                              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                              โ†“          โ†“          โ†“
                            async      await      Tasks
                                                    โ”‚
                                      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                                      โ†“             โ†“             โ†“
                                  gather()       wait()      wait_for()
                                                                    โ”‚
                                                                    โ†“
                                                                 Timeout
Enter fullscreen mode Exit fullscreen mode

29. Which One Should I Use? ๐Ÿงญ

Here's the simple decision-making guide.

Problem Good option
Normal simple program Regular synchronous code
Multiple I/O operations Threading or asyncio
Lots of network requests asyncio is often a great choice
File operations Threading or asyncio
Database operations Threading or asyncio
Heavy CPU calculations Multiprocessing
Need true CPU parallelism Multiprocessing
Shared data between threads Lock may be needed
Run multiple async operations asyncio.gather()
Check completed/pending tasks asyncio.wait()
Give an async operation a time limit asyncio.wait_for()
Stop an async task Cancellation

30. A Real-World Example ๐ŸŒ

Imagine you're building a web application.

A user sends a request:

GET /profile
Enter fullscreen mode Exit fullscreen mode

Your server needs to:

1. Query the database
2. Call another API
3. Download an image
4. Send the response
Enter fullscreen mode Exit fullscreen mode

Most of these operations involve waiting.

With asynchronous programming:

                  Event Loop
                      โ”‚
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ†“             โ†“             โ†“
     Request A     Request B     Request C
        โ”‚             โ”‚             โ”‚
      await          await         await
        โ”‚             โ”‚             โ”‚
        โ†“             โ†“             โ†“
     Database       API          Image
     waiting       waiting       waiting
Enter fullscreen mode Exit fullscreen mode

While one request is waiting, the event loop can work on another request.

This is why asynchronous programming is extremely useful for modern network applications.


31. The Most Important Rules to Remember ๐Ÿ“

You don't need to memorize everything at once.

Just remember these basic rules:

๐Ÿงต Thread

A path of execution inside a process.

๐Ÿงต Multithreading

Using multiple threads in one process.

๐Ÿญ Process

A running program with its own memory and resources.

๐Ÿญ Multiprocessing

Using multiple processes, especially useful for CPU-heavy work.

๐Ÿ”’ Lock

Prevents multiple threads from modifying shared data at the same time.

๐Ÿ˜ˆ GIL

In CPython, only one thread at a time executes Python bytecode within a process.

โšก Concurrency

Handling multiple tasks during overlapping periods.

๐Ÿš€ Parallelism

Multiple tasks actually executing at the same time.

๐ŸŒ I/O-bound

Mostly waiting for things like networks, databases, and files.

๐Ÿงฎ CPU-bound

Mostly doing calculations.

๐ŸŒŠ asyncio

Python's asynchronous programming framework for handling many I/O tasks efficiently.

๐Ÿ”„ Event Loop

Manages and runs asynchronous tasks.

๐Ÿงฉ Coroutine

A function that can pause and continue later.

async

Defines an asynchronous function.

await

Waits for an asynchronous operation while allowing other async tasks to run.

๐Ÿ“‹ Task

Scheduled asynchronous work.

๐Ÿ”ฎ Future

Represents a result that will be available later.

asyncio.gather()

Runs multiple async operations and collects their results.

asyncio.wait()

Lets you work with completed and pending tasks.

โฐ asyncio.wait_for()

Gives an async operation a time limit.

โŒ Cancellation

Stops an async task that is no longer needed.

๐Ÿค Cooperative Concurrency

Tasks give control back when they reach points where they can safely wait.


32. Final Mental Model ๐Ÿง 

If you remember nothing else, remember this:

                    YOUR PROGRAM
                         โ”‚
              "I have many things to do"
                         โ”‚
                         โ†“
                    CONCURRENCY
                         โ”‚
          โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
          โ†“              โ†“              โ†“
       THREADING    MULTIPROCESSING   ASYNCIO
          โ”‚              โ”‚              โ”‚
          โ†“              โ†“              โ†“
      Multiple       Multiple        Event Loop
      threads        processes          โ”‚
          โ”‚              โ”‚              โ†“
          โ†“              โ†“          Coroutines
        I/O            CPU              โ”‚
          โ”‚              โ”‚         async / await
          โ†“              โ†“              โ”‚
      Waiting        Calculating        โ†“
                                  Tasks / Futures
                                         โ”‚
                            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                            โ†“            โ†“            โ†“
                         gather       wait       wait_for
                                                     โ”‚
                                                     โ†“
                                                   timeout
Enter fullscreen mode Exit fullscreen mode

The biggest rule is:

I/O-bound โ†’ Threading / Asyncio

CPU-bound โ†’ Multiprocessing

And remember:

Concurrency is about managing multiple tasks.

Parallelism is about actually running multiple tasks at the same time.

Once these two ideas are clear, the rest of Python concurrency becomes much easier to understand.

Top comments (0)