In this Python tutorial, we learn about multithreading in Python using the threading module. You will see how to create and start a thread, pass arguments to a thread function, check whether a thread is alive, name threads, wait for threads with join(), and decide when Python threads are the right choice.
Python multithreading with the threading module
Python’s threading module allows you to run separate threads of execution inside the same Python process. A thread is useful when a program has tasks that spend time waiting, such as reading files, waiting for a network response, handling user input, or sleeping between scheduled checks.
Threads share the same memory space, so they can access the same variables and objects. This makes communication between threads convenient, but it also means you must be careful when multiple threads update shared data at the same time.
For most beginner Python programs, use the threading module for I/O-bound work. For CPU-heavy work that needs to use multiple CPU cores, the multiprocessing module or process-based executors are usually a better fit because of Python’s Global Interpreter Lock in the standard CPython runtime.
Example: create and start multiple Python threads
We shall look into a simple example using the threading module, and then go into the important thread operations one by one.
Note : The following examples are worked on environment with Python3 installed.
Following is a simple example to create multiple threads using threading module.
Python Program
import threading
def f():
print('Thread function\n')
return
for i in range(3):
t = threading.Thread(target=f)
t.start()
Output
Thread function
Thread function
Thread function
The order of output from threads is not guaranteed. In small examples, the output may look ordered, but in real programs different threads can complete at different times.
Create a thread in Python
You can create a thread in one of the two following ways.
1. Passing a method to Thread constructor.
def f():
print('Thread function\n')
return
t = threading.Thread(target=f)
Overriding run() method in a subclass of threading.Thread.
import threading
class CustomThread(threading.Thread):
def run(self):
print('Custom thread function.\n')
for i in range(3):
t = CustomThread()
The first approach, passing a target function to threading.Thread, is the most common and is usually enough for straightforward threaded tasks. Subclassing threading.Thread is useful when you want to group thread-specific state and behavior inside a class.
Start a Python thread using start()
A thread is started by applying start() method on the thread object.
Call start() only once on a thread object. The start() method schedules the thread and then internally calls the thread’s run() method. Do not call run() directly when you want a new thread, because that would run the function in the current thread instead.
Python Program
import threading
import time
def f():
print('Thread running.\n')
return
# start threads by passing function to Thread constructor
for i in range(3):
t = threading.Thread(target=f)
t.start()
Output
Thread running.
Thread running.
Thread running.
Pass arguments to a Python thread function
To pass arguments to the function supplied to Thread constructor, pass args in the Thread constructor as shown below :
The args parameter expects a tuple. If you pass only one argument, remember the trailing comma, as in args=(i,). You can also use kwargs to pass keyword arguments.
Python Program
import threading
import time
def f(i):
for p in range(3):
time.sleep(i+1)
print('Thread #',i,"\n")
time.sleep(i)
return
# start threads by passing function to Thread constructor
for i in range(3):
t = threading.Thread(target=f, args=(i,))
t.start()
Output
Thread # 0
Thread # 1
Thread # 0
Thread # 2
Thread # 0
Thread # 1
Thread # 1
Thread # 2
Thread # 2
Pass keyword arguments to threading.Thread in Python
When the thread function has named parameters, pass them through kwargs. This keeps the call readable when the function accepts more than one value.
Python Program
import threading
def download_file(file_name, retry_count=1):
print(f"Downloading {file_name}, retries allowed: {retry_count}")
thread = threading.Thread(
target=download_file,
kwargs={"file_name": "report.csv", "retry_count": 3}
)
thread.start()
thread.join()
Output
Downloading report.csv, retries allowed: 3
Wait for Python threads to finish using join()
The join() method makes the current thread wait until another thread finishes. In many programs, the main thread should start worker threads first and then join them so that the program does not exit before the work is complete.
Python Program
import threading
import time
def task(number):
time.sleep(1)
print(f"Task {number} completed")
threads = []
for i in range(3):
thread = threading.Thread(target=task, args=(i,))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
print("All threads completed")
Output
Task 0 completed
Task 1 completed
Task 2 completed
All threads completed
The task output order can change from one run to another, but All threads completed prints after all worker threads have finished because of join().
Check if a Python thread is alive
threading.Thread.is_alive() could be used to check if the thread is alive or not.
Thread.is_alive() returns True if the thread is alive, False if not alive.
A thread is alive after start() is called and before its run() method finishes. This is useful for monitoring long-running tasks or deciding whether a worker thread is still active.
Python Program
import threading
import time
def f(i):
time.sleep(i)
return
# threads
t1 = threading.Thread(target=f, args=(1.2,), name="Thread#1")
t1.start()
t2 = threading.Thread(target=f, args=(2.2,), name="Thread#2")
t2.start()
for p in range(5):
time.sleep(p*0.5)
print('[',time.ctime(),']', t1.getName(), t1.is_alive())
print('[',time.ctime(),']', t2.getName(), t2.is_alive())
Output
[ Tue Feb 27 17:58:54 2018 ] Thread#1 True
[ Tue Feb 27 17:58:54 2018 ] Thread#2 True
[ Tue Feb 27 17:58:55 2018 ] Thread#1 True
[ Tue Feb 27 17:58:55 2018 ] Thread#2 True
[ Tue Feb 27 17:58:56 2018 ] Thread#1 False
[ Tue Feb 27 17:58:56 2018 ] Thread#2 True
[ Tue Feb 27 17:58:57 2018 ] Thread#1 False
[ Tue Feb 27 17:58:57 ] Thread#2 False
[ Tue Feb 27 17:58:59 2018 ] Thread#1 False
[ Tue Feb 27 17:58:59 2018 ] Thread#2 False
In current Python code, prefer the name property instead of older getName() and setName() methods. The old methods still appear in older tutorials, but the property form is clearer.
Set and get Python thread names
Thread name could be set or read using setName() and getName() methods.
In a function being called inside a thread, to get the current thread, use threading.current_thread(). In the following example we shall use this method to get current thread object.
Python Program
import threading
import time
def f(i):
for p in range(3):
time.sleep(i+1.5)
print(threading.current_thread().getName())
return
# start threads by passing function to Thread constructor
for i in range(3):
t = threading.Thread(target=f, args=(i,))
t.setName( 'Thread#'+str(i) )
t.start()
Output
Thread#0
Thread#1
Thread#0
Thread#2
Thread#0
Thread#1
Thread#2
Thread#1
Thread#2
The same idea can be written with the name property.
Python Program
import threading
def show_thread_name():
current = threading.current_thread()
print(current.name)
thread = threading.Thread(target=show_thread_name, name="WorkerThread")
thread.start()
thread.join()
Output
WorkerThread
Protect shared data in Python multithreading with Lock
Because Python threads share memory, two or more threads may try to update the same variable at the same time. This can cause incorrect results. Use threading.Lock when only one thread should enter a critical section at a time.
Python Program
import threading
counter = 0
lock = threading.Lock()
def increment_counter():
global counter
for _ in range(10000):
with lock:
counter += 1
threads = []
for _ in range(5):
thread = threading.Thread(target=increment_counter)
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
print(counter)
Output
50000
The with lock: block acquires the lock before updating counter and releases it after the update. This pattern is preferred because the lock is released even if an exception occurs inside the block.
Daemon threads in Python threading
A daemon thread runs in the background and does not keep the Python program alive by itself. When only daemon threads are left, the program can exit. Use daemon threads for background helper work where abrupt shutdown is acceptable. Do not use them for important work that must be completed or safely saved.
Python Program
import threading
import time
def background_task():
while True:
print("Background task running")
time.sleep(1)
thread = threading.Thread(target=background_task, daemon=True)
thread.start()
time.sleep(2.5)
print("Main program ends")
The daemon thread is not joined here. Once the main program ends, Python does not wait for this background thread to finish its infinite loop.
When does a Python thread stop?
A thread stops when :
- run() method terminates normally. [or]
- an unhandled exception causes run() method to terminate abruptly.
Python does not provide a safe general-purpose method to forcibly stop a thread from the outside. A common design is to let the thread check a shared flag or a threading.Event and return cleanly when cancellation is requested.
Python Program
import threading
import time
stop_event = threading.Event()
def worker():
while not stop_event.is_set():
print("Working...")
time.sleep(0.5)
print("Worker stopped")
thread = threading.Thread(target=worker)
thread.start()
time.sleep(2)
stop_event.set()
thread.join()
Python threading module methods used in this tutorial
| Threading feature | Purpose in Python multithreading |
|---|---|
threading.Thread(target=...) | Creates a thread object that will run the given function. |
start() | Starts thread execution in a separate thread. |
join() | Waits for a thread to finish. |
args and kwargs | Passes positional and keyword arguments to the thread function. |
is_alive() | Checks whether the thread is still running. |
current_thread() | Returns the thread object for the currently running thread. |
Lock | Protects shared data from concurrent updates. |
Event | Coordinates state between threads, often for stopping workers cleanly. |
When to use Python multithreading
Python multithreading is a good choice when the program spends a noticeable amount of time waiting. Common examples include downloading several URLs, polling APIs, reading many files, writing logs in the background, or running a responsive user interface while another task waits.
Python multithreading is usually not the best choice for pure CPU-heavy tasks such as large numeric calculations, image processing loops, or compression loops that need parallel CPU execution. For those cases, consider multiprocessing, native libraries that release the GIL, or concurrent execution with separate processes.
Common mistakes in Python threading code
- Calling
run()directly instead ofstart(), which prevents the task from running in a separate thread. - Forgetting
join()when the main thread must wait for worker threads to complete. - Updating shared variables from multiple threads without a
Lockor another synchronization method. - Assuming thread output order will be the same every time.
- Using daemon threads for work that must finish safely, such as saving data to a file.
Frequently asked questions about Python multithreading
Which Python module is used for multithreading?
The threading module is the standard high-level Python module used for multithreading. It provides the Thread class and synchronization tools such as Lock, Event, Condition, and Semaphore.
Does Python really support multithreading?
Yes. Python supports multithreading through the threading module. In the standard CPython runtime, the Global Interpreter Lock affects CPU-bound parallel execution, but threads are still useful for I/O-bound tasks where the program waits for files, network calls, timers, or other external operations.
How do you use the threading module in Python?
Import threading, create a threading.Thread object with a target function, call start() to begin execution, and call join() if the main thread must wait for it to finish. Use args or kwargs to pass values to the target function.
What is the difference between start() and run() in Python threading?
start() begins execution in a new thread and then calls run() internally. Calling run() directly executes the code in the current thread, so it does not create real multithreaded execution.
Should I use threads for CPU-bound Python programs?
Usually, no. For CPU-bound work that needs parallel execution across CPU cores, use multiprocessing or process-based concurrency. Threads are generally better suited for I/O-bound work in Python.
Python multithreading editorial QA checklist
- Confirm that every Python threading example imports
threadingwhen it is run as a standalone program. - Check that new worker-thread examples use
start()and, where needed,join(). - Make sure output blocks are marked with the
outputclass and Python source blocks uselanguage-python. - Verify that examples involving shared data use
Lockor clearly explain why synchronization is not needed. - Keep the explanation clear that Python threads are useful for I/O-bound tasks, while CPU-bound parallelism usually needs another approach.
Conclusion
In this Python Tutorial, we learned about multithreading using threading Python package. We created threads, started them with start(), passed arguments, checked thread status with is_alive(), used thread names, waited with join(), and protected shared data with Lock.
TutorialKart.com