Stop by Stop

Concurrency line

Threading deep-dive

A cook can be hired on paper before he walks in, and you wait at the pass until his shift ends.

Words to know

Stops explain each step. The film plays the whole ride.

Explain it
Route
The contractPaper onlyHis counterKitchen emptyThe stoveThe recipeThe doorA deliveryThe passPass is quiet
  • Walking in or cooking
  • Waiting
  • Coming back or finishing
  • Cut off
Stop 1 of 6

The contract is only paper

The cook is hired on paper. Nothing is running, and he is not in the kitchen yet.

Next stop: He walks into the kitchen

All stops

The everyday version

One cook. The contract is the hire. Walking in starts the shift at his own counter. The door is a delivery. The pass is you, waiting until he is done.

  • The contractis the Thread object

    The hire on paper. He is not cooking yet.

  • His counteris the OS thread

    His own bench. Calls and local notes stay here, not on a shared pile.

  • The stoveis the Interpreter

    Where the recipe runs. On a normal setup, one cook at a time.

  • The dooris the Blocked call

    A delivery. He is not cooking, so someone else may take the stove.

  • The passis the join

    You, waiting until the shift ends.

  • A second cookis the next page

    Two cooks and one tally. That fight is not this page.

Interview questions

Say the short answer first. Go deeper only if they ask.

What is the difference between the Thread object and the OS thread?
Short answer

The object can exist before the cook walks in. The real thread begins then.

If they dig deeper

ident is None until start. is_alive tracks run, from just before it until just after it returns, not the lifetime of the object. Creating a bare Thread does not start it. Submitting to a ThreadPoolExecutor does start work.

Answer that loses points

“Creating the Thread starts it.”

What does daemon mean?
Short answer

The process will not stay up just for that thread.

If they dig deeper

The process exits when no non-daemon thread is alive, and daemon threads are stopped abruptly. Set the flag before start. A clean stop is a non-daemon thread plus an Event. Joining a daemon during late finalization can raise PythonFinalizationError in 3.14.

Answer that loses points

“Daemon means low priority.”

Why does each thread have a stack?
Short answer

So its calls and local notes are not mixed with another cook’s.

If they dig deeper

Threads share the heap. The stack is private. stack_size(0) keeps the platform default for threads created afterward, and the smallest size you may set is 32 KiB. The docs do not state an 8 MB default.

Answer that loses points

“Threads share one stack.”

What does join actually tell you?
Short answer

It waits until the shift ends, and it returns nothing.

If they dig deeper

join returns None. A timeout does not mean the thread finished: call is_alive afterward. Joining yourself, or joining before start, raises RuntimeError. An exception inside run is handed to threading.excepthook, which ignores SystemExit.

Answer that loses points

“join returns the thread’s result.”

What is a thread that Python did not start?
Short answer

It shows up as a dummy. You cannot wait for it at the pass.

If they dig deeper

A thread started outside the threading module becomes a dummy when Python notices it. Dummies are always alive, always daemonic, and cannot be joined. Mention them once. Do not build a design around joining one.

Answer that loses points

“Every thread you can see can be joined.”

Cheat-sheet

Commands you will actually type.

t = threading.Thread(target=work)
print(t.ident, t.is_alive())  # None False

The object exists. No kernel thread yet, and it is not alive.

t.daemon = True  # before start, or RuntimeError
t.start()

The flag is inherited, and the main thread is not a daemon.

t.join(1.0)  # returns None
still = t.is_alive()

A timeout does not say the thread finished. Ask is_alive after it.

threading.stack_size(0)  # platform default
# smallest settable size is 32 KiB

Zero keeps the platform default. Do not assume 8 MB.

stop = threading.Event()
# non-daemon thread: stop.wait() until stop.set()

A clean stop. A daemon is cut off when the process exits.

Sources