How do I pause main until all other threads have died?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
If the main thread needs to wait until worker threads finish, the usual answer is not to "pause" main in some vague way. The correct answer is to join the worker threads or wait on a synchronization primitive that represents their completion. In most languages, that is the normal, explicit way to keep the process alive until work is done.
Join Threads Explicitly
The simplest pattern is to keep references to the worker threads and call join() on each one. join() blocks the calling thread until the target thread exits.
In Python:
The same concept exists in C++ with std::thread:
That is the direct answer in most ordinary programs.
Why Sleeping Main Is the Wrong Pattern
Beginners sometimes try sleep() loops to keep main alive. That is unreliable because you are guessing how long the threads will take instead of synchronizing with their real completion.
If the workers take longer than expected, main may exit too early. If they finish quickly, the program waits longer than necessary. join() solves both problems because it waits for actual thread termination rather than elapsed time.
When a Coordination Primitive Is Better
If you do not have direct thread references, or if you are coordinating larger phases of work, a primitive such as a countdown latch, condition variable, barrier, or future can be a better fit.
For example, in Python's concurrent.futures, waiting on futures often expresses the intent more clearly than managing raw threads yourself:
Here future.result() waits for completion, and the executor context also ensures orderly shutdown.
Watch Out for Daemon Threads
Some runtimes allow daemon-style threads that do not keep the process alive. If you mark threads as daemon threads, the program may exit while they are still running. That behavior is sometimes intentional, but it is the opposite of waiting for all work to finish.
If your requirement is "do not let the process exit before workers are done," daemon threads are usually not what you want.
Common Pitfalls
- Using
sleep()instead of waiting on actual thread completion. - Starting threads and then losing the references needed to join them.
- Forgetting that daemon threads may be terminated when the process exits.
- Joining only some worker threads and assuming the rest are finished too.
- Using raw threads when futures or executors would express the workflow more clearly.
Summary
- The normal way to make main wait is to join worker threads.
- '
join()blocks until a specific thread exits.' - Futures and higher-level executors are often clearer for structured concurrency.
- Sleeping main is not real synchronization.
- Keep explicit ownership of worker completion if the process must not exit early.
Related reading
- How Do I Queue My Python Locks?
- How do I replicate content on a web farm
- How do I return the response from an asynchronous call?
- How do I run a simple bit of code in a new thread?
- How do I run a simple bit of code in a new thread?
- How do I run Asynchronous callbacks in Playground
- How do I send something to connected websocket clients from another thread?
- How do I stop a thread when my winform application closes
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.