thread management
multithreading
main function
thread synchronization
programming

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.

Browse interview questions

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:

python
1import threading
2import time
3
4
5def worker(name: str) -> None:
6    time.sleep(1)
7    print(f"{name} done")
8
9
10threads = [threading.Thread(target=worker, args=(f"worker-{i}",)) for i in range(3)]
11
12for thread in threads:
13    thread.start()
14
15for thread in threads:
16    thread.join()
17
18print("main can exit now")

The same concept exists in C++ with std::thread:

cpp
1#include <iostream>
2#include <thread>
3#include <vector>
4
5void worker(int id) {
6    std::cout << "worker " << id << " done\n";
7}
8
9int main() {
10    std::vector<std::thread> threads;
11
12    for (int i = 0; i < 3; ++i) {
13        threads.emplace_back(worker, i);
14    }
15
16    for (auto &t : threads) {
17        t.join();
18    }
19
20    std::cout << "main can exit now\n";
21}

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:

python
1from concurrent.futures import ThreadPoolExecutor
2
3
4def work(x: int) -> int:
5    return x * x
6
7
8with ThreadPoolExecutor(max_workers=4) as executor:
9    futures = [executor.submit(work, i) for i in range(5)]
10    results = [future.result() for future in futures]
11
12print(results)

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
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track 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.

Browse interview questions

All Rights Reserved.