Multithreading in Python
Multithreading allows a program to run multiple tasks at the same time by using separate threads. This helps increase efficiency when tasks involve waiting, such as downloading data or reading files. It improves responsiveness and avoids blocking the main program.
What Is a Thread?
A thread is a lightweight unit of execution that runs inside a program. Each thread can perform a task independently while sharing the same memory space. This makes threading efficient for input/output-heavy operations.
Why Use Multithreading?
It allows parts of a program to run simultaneously instead of waiting for one another. It makes applications feel faster and more responsive, especially during long operations. It is commonly used in network requests, file handling, and user interface applications.
Creating a Simple Thread
Python provides the threading module to create and manage threads.
Below is an example showing how to start a thread that runs a function separately.
This helps perform background tasks without stopping the main program.
import threading
def greet():
print("Hello from a thread!")
t = threading.Thread(target=greet)
t.start()
print("Main program continues")
Threads with Arguments
A thread can receive arguments just like a normal function. This is helpful when you need multiple threads performing similar actions with different values. It keeps your code flexible and scalable.
import threading
def repeat(message):
print("Message:", message)
t = threading.Thread(target=repeat, args=("Working...",))
t.start()
Multithreading Example with Sleep
The time.sleep() function simulates a long-running task such as loading or processing.
Using threads, these delays do not freeze the entire program.
This demonstrates how threads allow smooth, non-blocking execution.
import threading, time
def task():
time.sleep(2)
print("Task complete")
thread = threading.Thread(target=task)
thread.start()
print("Main thread running...")
Joining Threads
The .join() method forces the program to wait until a thread finishes.
This is useful when later operations depend on the completion of threaded tasks.
It prevents out-of-order execution when synchronization is needed.
import threading
import time
def process():
time.sleep(1)
print("Process finished")
t = threading.Thread(target=process)
t.start()
t.join()
print("All tasks done")
Running Multiple Threads
You can run several threads at once to perform repeated operations efficiently. This pattern is commonly used for downloading multiple files or processing lists of inputs. Each thread runs independently but shares the same memory space.
import threading
def show(i):
print("Thread number:", i)
threads = []
for i in range(5):
t = threading.Thread(target=show, args=(i,))
threads.append(t)
t.start()
Limitations of Multithreading (GIL)
Python has a mechanism called the Global Interpreter Lock (GIL). It allows only one thread to execute Python bytecode at a time. This means multithreading does not improve performance in CPU-heavy tasks.
However, multithreading works extremely well for I/O-heavy tasks such as downloading, reading files, or waiting for user interaction. It keeps applications responsive without needing parallel computation.
📝 Practice Exercises
Exercise 1
Create a thread that prints “Running...” three times with a delay.
Exercise 2
Create two threads: one prints numbers, another prints letters.
Exercise 3
Write a program that creates 5 threads, each printing a unique ID.
Exercise 4
Use .join() to ensure a thread finishes before printing a final message.
✅ Practice Answers
Answer 1
import threading, time
def run():
for _ in range(3):
print("Running...")
time.sleep(1)
t = threading.Thread(target=run)
t.start()
Answer 2
import threading
import time
def numbers():
for i in range(5):
print(i)
time.sleep(0.5)
def letters():
for c in ["A", "B", "C", "D", "E"]:
print(c)
time.sleep(0.5)
t1 = threading.Thread(target=numbers)
t2 = threading.Thread(target=letters)
t1.start()
t2.start()
Answer 3
import threading
def display(i):
print("ID:", i)
for i in range(5):
threading.Thread(target=display, args=(i,)).start()
Answer 4
import threading
import time
def load():
time.sleep(2)
print("Loading complete")
t = threading.Thread(target=load)
t.start()
t.join()
print("Program finished")