Course

Python Multithreading: Syntax, Usage, and Examples

Python multithreading allows you to run multiple threads in parallel within a single process. This is especially useful for I/O-bound tasks like handling multiple web requests, reading files, or making API calls. Python’s threading module makes it easy to create and manage threads for concurrent execution. However, because of Python’s Global Interpreter Lock (GIL), multithreading doesn’t improve performance for CPU-intensive tasks.


How to Use Python Multithreading

Python provides the threading module for working with threads. To create and start a new thread, you define a function and pass it as the target to a Thread object.

Creating and Running Threads

python
import threading def print_numbers(): for i in range(1, 6): print(i) # Creating a thread thread = threading.Thread(target=print_numbers) # Starting the thread thread.start() # Waiting for the thread to finish thread.join()
  • threading.Thread(target=function_name): Creates a thread that runs the specified function.
  • .start(): Begins execution of the thread.
  • .join(): Blocks the main thread until the created thread finishes execution.

When to Use Multithreading in Python

Multithreading is useful when you want to perform multiple tasks at the same time, especially when those tasks involve waiting. Here are three common scenarios where Python multithreading shines:

1. Handling Multiple User Requests in Web Applications

If you’re building a web application, you might need to process multiple user requests simultaneously. Multithreading ensures that one slow request doesn’t block others.

python
import threading def handle_request(user_id): print(f"Processing request for user {user_id}") # Creating multiple threads threads = [threading.Thread(target=handle_request, args=(i,)) for i in range(1, 4)] for thread in threads: thread.start() for thread in threads: thread.join()

2. Downloading Multiple Files Simultaneously

You can download multiple files at once instead of waiting for each to finish before starting the next.

python
import threading import time def download_file(file_name): print(f"Downloading {file_name}...") time.sleep(2) # Simulating download time print(f"{file_name} downloaded.") files = ["file1.txt", "file2.txt", "file3.txt"] threads = [threading.Thread(target=download_file, args=(file,)) for file in files] for thread in threads: thread.start() for thread in threads: thread.join()

3. Keeping a GUI Responsive While Running Background Tasks

In GUI applications, long-running tasks like data processing can make the interface unresponsive. You can use multithreading to keep the UI active while processing data in the background.

python
import threading import time import tkinter as tk def load_data(): time.sleep(3) # Simulating a slow operation label.config(text="Data Loaded!") root = tk.Tk() root.geometry("200x100") label = tk.Label(root, text="Loading...") label.pack() # Running the task in a separate thread thread = threading.Thread(target=load_data) thread.start() root.mainloop()

Examples of Python Multithreading

Example 1: Running Multiple Tasks Concurrently

python
import threading def task(name): for _ in range(3): print(f"Task {name} is running") threads = [threading.Thread(target=task, args=(i,)) for i in range(1, 4)] for thread in threads: thread.start() for thread in threads: thread.join()

Example 2: Preventing Race Conditions with Locks

When multiple threads access the same variable, they can interfere with each other. A lock prevents this problem.

python
import threading counter = 0 lock = threading.Lock() def increment(): global counter for _ in range(100000): with lock: # Ensuring only one thread modifies the counter at a time counter += 1 threads = [threading.Thread(target=increment) for _ in range(2)] for thread in threads: thread.start() for thread in threads: thread.join() print(f"Final counter value: {counter}")

Example 3: Using a Thread Pool for Efficient Task Execution

If you need to run many small tasks, a thread pool can manage threads more efficiently.

python
import concurrent.futures def square_number(n): return n * n numbers = [1, 2, 3, 4, 5] with concurrent.futures.ThreadPoolExecutor() as executor: results = executor.map(square_number, numbers) print(list(results)) # Output: [1, 4, 9, 16, 25]
  • ThreadPoolExecutor automatically manages a pool of worker threads, reducing the overhead of creating and destroying threads.

Learn More About Python Multithreading

Python Multithreading vs. Multiprocessing

Python multithreading is great for I/O-bound tasks, such as network requests, database queries, and file I/O. However, because of the Global Interpreter Lock (GIL), only one thread runs Python code at a time, meaning multithreading won’t speed up CPU-intensive tasks like image processing or machine learning computations.

If you need to maximize CPU usage, use multiprocessing instead.

Using the Python Multithreading Pool

A thread pool efficiently handles multiple threads when you have many short tasks.

python
import concurrent.futures def process_data(item): return item * 2 data = [10, 20, 30, 40] with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor: results = executor.map(process_data, data) print(list(results)) # Output: [20, 40, 60, 80]
  • max_workers=2: Limits the number of threads running at once.

Python Multithreading Libraries

Python provides several libraries for working with multithreading:

  • threading – The built-in module for managing threads.
  • concurrent.futures – Simplifies working with multiple threads using ThreadPoolExecutor.
  • queue – Provides thread-safe data structures like Queue, making it easier to share data between threads.

When to Avoid Multithreading in Python

Multithreading is not helpful for CPU-intensive tasks because of the GIL. For example, if you need to:

  • Perform mathematical calculations on large datasets.
  • Process images, videos, or audio files.
  • Train machine learning models.

In these cases, use the multiprocessing module instead.


Python multithreading is a powerful tool for improving performance in I/O-bound applications. You can use it to handle multiple user requests, download files in parallel, and keep GUI applications responsive. However, if your goal is to speed up CPU-heavy computations, multithreading won’t help—multiprocessing is the better choice.