Introduction ๐
Threading allows a Python program to run multiple tasks concurrently within the same process. It is extremely useful for I/O-bound tasks such as network requests, file operations, timers, and user interaction.
Note
๐ก Best for I/O-bound tasks โ NOT CPU-bound tasks (because of the GIL)
๐ก Managed using the
threading module1. Importing the Threading Module ๐งฑ
import_threading.py
import threading2. Creating and Starting a Thread โถ๏ธ
basic_thread.py
import threading
import time
def greet():
print("Hello from thread!")
time.sleep(1)
t = threading.Thread(target=greet)
t.start()
t.join() # wait for thread to finishโ target โ function to run
โ start() โ launches thread
โ join() โ wait for it to finish
3. Passing Arguments to Threads ๐ฏ
thread_args.py
def show(name):
print(f"Hello {name}")
t = threading.Thread(target=show, args=("Sathish",))
t.start()
t.join()โ Use args or kwargs
4. Running Multiple Threads Concurrently โก
multiple_threads.py
def worker(num):
print(f"Working on task {num}")
threads = []
for i in range(5):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join()โ All threads run concurrently
5. Using Threading Class (Subclassing) ๐๏ธ
thread_class.py
class MyThread(threading.Thread):
def run(self):
print("Thread running...")
t = MyThread()
t.start()
t.join()โ Override run() for custom behavior
6. Daemon Threads ๐ป
A **daemon thread** runs in the background and exits when the main program ends.
daemon_thread.py
import time, threading
def task():
while True:
print("Background task running...")
time.sleep(1)
t = threading.Thread(target=task, daemon=True)
t.start()
time.sleep(3)
print("Main program ending...")โ Daemon threads stop automatically when the program stops
7. Thread Synchronization with Lock ๐
Threads share memory โ risk of race conditions. Use Lock to prevent unsafe simultaneous access.
lock_example.py
lock = threading.Lock()
counter = 0
def increment():
global counter
with lock:
temp = counter
temp += 1
counter = temp
threads = []
for _ in range(1000):
t = threading.Thread(target=increment)
threads.append(t)
t.start()
for t in threads:
t.join()
print(counter)โ with lock ensures thread-safe operations
8. Using RLock (Reentrant Lock) ๐
Allows the same thread to acquire the lock multiple times.
rlock_example.py
rlock = threading.RLock()
def task():
with rlock:
with rlock: # same thread re-enters
print("Inside RLock")
t = threading.Thread(target=task)
t.start()
t.join()9. Using Condition for Thread Coordination ๐น๏ธ
condition_example.py
condition = threading.Condition()
data_ready = False
def producer():
global data_ready
with condition:
data_ready = True
print("Producer: Data ready")
condition.notify()
def consumer():
with condition:
condition.wait()
print("Consumer: Received data")
threading.Thread(target=consumer).start()
threading.Thread(target=producer).start()โ Great for producerโconsumer workflow
10. Using Event for Signaling ๐ฆ
event_example.py
event = threading.Event()
def waiter():
print("Waiting for event...")
event.wait()
print("Event triggered!")
threading.Thread(target=waiter).start()
input("Press Enter to trigger event...")
event.set()โ Event helps coordinate threads
11. Thread Pool with concurrent.futures ๐ผ
thread_pool.py
from concurrent.futures import ThreadPoolExecutor
def task(n):
return n * 2
with ThreadPoolExecutor(max_workers=4) as executor:
results = executor.map(task, [1,2,3,4])
print(list(results))โ Simplest way to manage many threads
12. GIL (Global Interpreter Lock) โ Important โ ๏ธ
Python threads cannot run **CPU-bound tasks** in parallel due to the GIL. Use threading only for:
- โ I/O-bound tasks (network, disk, waiting)
- โ Background tasks
Note
13. Real-World Example โ Downloading Files Concurrently ๐
download_example.py
import threading, time
def download(file):
print(f"Downloading {file}")
time.sleep(2)
print(f"Finished {file}")
files = ["a.jpg", "b.jpg", "c.jpg"]
threads = [threading.Thread(target=download, args=(f,)) for f in files]
for t in threads: t.start()
for t in threads: t.join()Threading Cheat Sheet ๐
| Feature | Method / Object |
|---|---|
| Create thread | Thread(target=...) |
| Start thread | start() |
| Wait for thread | join() |
| Daemon thread | daemon=True |
| Synchronization | Lock(), RLock() |
| Coordination | Event(), Condition() |
| Thread Pool | ThreadPoolExecutor |
Best Practices ๐ก
- โ Use threading only for I/O-bound tasks
- โ Always use locks for shared resources
- โ Prefer ThreadPoolExecutor for many tasks
- โ Avoid long-running daemon threads
- โ Use logging instead of print() in threaded apps
Conclusion ๐
You now understand Threading in Python! Want the next topic? Try Multiprocessing, AsyncIO, Concurrency Models, or GIL Explained. Just tell me! ๐