What is Multithreading?

Multithreading runs multiple threads concurrently. In Python, it's best for I/O-bound tasks due to the Global Interpreter Lock (GIL).

Basic Threading

Use the threading module:

import threading
import time

def task(name, delay):
    print(f"Task {name} starting")
    time.sleep(delay)
    print(f"Task {name} finished")

thread1 = threading.Thread(target=task, args=("A", 2))
thread2 = threading.Thread(target=task, args=("B", 1))

thread1.start()
thread2.start()

thread1.join()
thread2.join()

ThreadPoolExecutor

Easier thread management:

from concurrent.futures import ThreadPoolExecutor

def download(url):
    time.sleep(1)
    return f"Downloaded {url}"

urls = ["url1", "url2", "url3"]

with ThreadPoolExecutor(max_workers=3) as executor:
    results = executor.map(download, urls)
    for result in results:
        print(result)

Thread Safety

Use locks to prevent race conditions:

import threading

counter = 0
lock = threading.Lock()

def increment():
    global counter
    with lock:
        counter += 1

When to Use

  • I/O-bound tasks: Network, files
  • Concurrent operations: Multiple independent tasks
  • Avoid for CPU-bound: Use multiprocessing instead

Best Practices

  • Use ThreadPoolExecutor: Easier than manual thread management
  • Use locks: When sharing data between threads
  • Don't overuse: Threads have overhead
  • Consider async: Often simpler for I/O operations

Next Steps

Learn about multiprocessing for CPU-bound tasks.