Starting threads

threading & multiprocessing · Essentials · 9 min

Threads are the simplest way to overlap slow I/O such as downloads, disk access and API calls.

A thread is an independent flow of execution inside your process. All threads share the same memory: globals, objects, open files.

t = threading.Thread(target=download, args=(url,), name="dl-1")
t.start()     # begins running download(url) in the new thread
...
t.join()      # wait until it finishes

Key points:

  • start() returns immediately; the function runs in the background. Calling run() directly would execute it in the current thread, which is a common mistake.
  • join(timeout=None) blocks until the thread ends. Always join threads you start, or know why you don't.
  • A thread's return value is discarded. Pass results back through a shared structure, a queue, or use an executor (which returns futures).
  • Exceptions in a thread don't propagate to the starter; they're printed via threading.excepthook and the thread dies.
  • threading.current_thread().name and threading.get_ident() identify the running thread; threading.active_count() counts live ones.

Threads are cheap compared with processes but not free (each has its own stack). For many short tasks, reuse a fixed set of threads with a ThreadPoolExecutor instead of creating one thread per task.

Example

import threading, time

results = {}

def fetch(name, delay):
    time.sleep(delay)                       # stands in for network I/O
    results[name] = f"{name} done by {threading.current_thread().name}"

start = time.perf_counter()
threads = [threading.Thread(target=fetch, args=(f"job{i}", 0.5), name=f"worker-{i}")
           for i in range(3)]
for t in threads:
    t.start()
for t in threads:
    t.join()

print(results["job1"])                       # job1 done by worker-1
print(f"{time.perf_counter() - start:.1f}s") # 0.5s, not 1.5s

Remember

  • Thread(target=f, args=(...)).start() runs f concurrently
  • join() waits for the thread to finish
  • Return values and exceptions don't come back automatically

Watch out for

  • Calling t.run() instead of t.start(), which runs the function synchronously in the current thread.

Check yourself

  1. What does t.start() return while the target function is still running?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/threading.html#thread-objects