0Pricing
Python Academy · Lesson

Threads and the GIL

Understand Python threading limits.

Threads and the GIL is a free Python Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Python Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Is a Thread?

A thread is a separate flow of execution inside one process. Python's threading module lets you run functions concurrently within a single program.

import threading

def worker():
    print('hello from thread')

t = threading.Thread(target=worker)
t.start()
t.join()
print('main done')

Starting Multiple Threads

You can launch several threads. Each runs its target function. join() makes the main thread wait until a thread finishes.

import threading

def task(name):
    print('task', name)

threads = [threading.Thread(target=task, args=(i,)) for i in range(3)]
for t in threads:
    t.start()
for t in threads:
    t.join()
print('all finished')

The Global Interpreter Lock

CPython has a Global Interpreter Lock (GIL): only one thread executes Python bytecode at a time. This means threads do not give true parallel speedup for pure-Python CPU work.

import sys
print('Running on:', sys.implementation.name)
print('Only one thread runs Python bytecode at a time due to the GIL')

Where Threads Still Help

Threads shine for I/O-bound work: network requests, disk reads, waiting. While one thread waits, the GIL is released and another can run.

import threading, time

def fake_io(name):
    time.sleep(0.1)
    print('done', name)

start = time.perf_counter()
ts = [threading.Thread(target=fake_io, args=(i,)) for i in range(3)]
for t in ts: t.start()
for t in ts: t.join()
print('elapsed about 0.1s, not 0.3s')

CPU-Bound Work Does Not Speed Up

For heavy computation, adding threads will not make it faster because the GIL serializes them. For CPU work, use multiprocessing instead.

import threading

def burn():
    total = 0
    for i in range(1000000):
        total += i
    return total

t = threading.Thread(target=burn)
t.start()
t.join()
print('CPU-bound: threads share one core under the GIL')

Daemon Threads

A daemon thread is killed automatically when the main program exits. Use it for background helpers you do not need to wait for.

import threading, time

def background():
    time.sleep(0.05)
    print('background tick')

t = threading.Thread(target=background, daemon=True)
t.start()
t.join()
print('main exits')

Naming and Identifying Threads

Each thread has a name and id. threading.current_thread() returns the running thread object, handy for logging.

import threading

def show():
    t = threading.current_thread()
    print('running in', t.name)

t = threading.Thread(target=show, name='Worker-1')
t.start()
t.join()
print('main thread:', threading.current_thread().name)

Subclassing Thread

Instead of passing a target, you can subclass Thread and override run(). This is useful when a thread needs its own state.

import threading

class Greeter(threading.Thread):
    def __init__(self, name):
        super().__init__()
        self.who = name
    def run(self):
        print('hi', self.who)

g = Greeter('Sam')
g.start()
g.join()

Returning Results From Threads

A target function's return value is discarded. To collect results, write them into a shared structure (later you'll see safer tools like queues).

import threading

results = {}

def compute(key):
    results[key] = key * key

ts = [threading.Thread(target=compute, args=(i,)) for i in range(4)]
for t in ts: t.start()
for t in ts: t.join()
print(results)

Counting Active Threads

threading.active_count() tells how many threads are currently alive, including the main thread.

import threading, time

def wait():
    time.sleep(0.05)

ts = [threading.Thread(target=wait) for _ in range(2)]
for t in ts: t.start()
print('active now:', threading.active_count())
for t in ts: t.join()
print('active after join:', threading.active_count())

Choosing Threads vs Processes

Rule of thumb: use threads for I/O-bound concurrency and processes for CPU-bound parallelism. The GIL is the reason.

tasks = {'download files': 'threads', 'crunch numbers': 'processes', 'call APIs': 'threads'}
for task, choice in tasks.items():
    print(task, '->', choice)

Quick Check

Test your understanding of threads and the GIL.

Recap

You learned about threads and the GIL:

  • Threads run concurrently inside one process via threading.Thread.
  • The GIL serializes Python bytecode execution.
  • Threads help I/O-bound work; processes help CPU-bound work.
  • Use start(), join(), daemon threads, and subclassing.

Next: managing threads cleanly with ThreadPoolExecutor.

Frequently asked questions

Is the “Threads and the GIL” lesson free?

Yes — the full text of “Threads and the GIL” is free to read here on the web, and the Python Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Python Academy course, upgrade to CoddyKit PRO.

What will I learn in “Threads and the GIL”?

Understand Python threading limits. You practise Python Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Python Academy?

No prior experience is required. Python Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Threads and the GIL” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Python Academy lesson?

Yes. Every Python Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Threads and the GIL
  2. ThreadPoolExecutor
  3. multiprocessing Basics
  4. Sharing Data Safely
← Back to Python Academy