The concurrency round

A whole round on threads: build a component that is safe under them, or find what is wrong with a program that is not. What it scores, how it is usually lost, and the habits that win it, each one shown on code the build runs.

What happens in a concurrency interview round?

It is a whole round, usually 45 to 60 minutes, in a shared editor where the code runs. It comes in two formats. In the first you are given a spec and asked to build a component that stays correct under threads: a blocking queue, a cache, a rate limiter, a scheduler. In the second you are handed a concurrent program and asked what is wrong with it. A third version is not a separate round at all: a practical coding question is built single threaded first, and the last stage is the interviewer asking you to make it safe under threads.

Should I write a lock-free design to impress the interviewer?

No. The failure reported most often in this round is the candidate who starts a clever design, spends the second half of the hour on its bugs, and has only ever tested it with one thread. A plain lock, with a condition variable when somebody has to wait, is often the answer the interviewer expects. In Python there is a further reason: the standard library gives you no compare-and-swap to build a lock-free structure from. Get the simple version correct and tested, then name the finer-grained design as the step you would take if a measurement showed the lock was contended.

How do I test threaded code in a coding interview?

Four habits. Start every thread behind one threading.Barrier, so they are all running at the same moment rather than one after another. Use at least two threads on each side, two producers and two consumers, because many concurrency bugs need two threads doing the same job to show. Assert an invariant that holds in every interleaving, such as every item arriving exactly once, never a timing or an order the scheduler happens to pick. And join every thread before the test returns. A passing test like that is evidence, not proof, so say the argument for correctness out loud as well.

How do I explain why my design cannot deadlock?

Say the rule and show where the code keeps it. A deadlock needs a ring of threads, each holding one lock while it waits for the next, so the argument is about which locks can be held together. Either no thread ever holds two locks at once, or every thread that does takes them in one global order, such as sorted by name. Then check the part people miss: no lock is held while calling out to code that takes other locks, like a callback or a method on another object. If the design passes both checks, there is no ring to form.

When should I use a Condition instead of a Lock or a Queue?

Use a Lock when threads only need to take turns with some shared state. Use a Condition when a thread has to wait until that state reaches some value, such as a count reaching zero or a buffer having room, because the wait and the state have to share one lock. Use queue.Queue when the job is handing work from one thread to another, because it already does the locking and the waiting. Always put a condition wait in a while loop, or use wait_for, since a woken thread has to look again before it acts.