Design and Implement a Worker Thread Pool with Futures and Shutdown

Read the full interview experience this question came from →

Quick Overview

Low-level design and concurrency question: design and implement a fixed-size thread pool with task submission, futures that return results or errors, and graceful or cancelling shutdown. It tests lock and condition variable use, lost wake-ups, policies for rejected work, pool sizing and deadlocks between dependent tasks.

Design and Implement a Worker Thread Pool with Futures and Shutdown

Company: Microsoft

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Other

Design and implement a thread pool: a fixed set of worker threads that run tasks submitted by other threads, so the application does not start a new thread for every task. Model the classes first, then implement the parts where concurrency bugs live: submitting a task, the worker loop, delivering results and errors back to the submitter, and shutting the pool down. As in other low-level design rounds, the interviewer looks for clear class responsibilities and correct implementations of the key methods rather than every helper. ### Constraints and Clarifications - Any thread may submit tasks at any time, including code that is itself running inside the pool. - The submitter must be able to wait for a task's result and must see any exception the task raised. - Shutdown must not leave worker threads running or callers waiting forever. - You may use the language's mutexes, condition variables and threads, but not its built-in executor or thread-pool classes. ### Clarifying Questions - Is the number of workers fixed at creation, or should it grow and shrink with load? - Should the task queue be bounded, and what should happen to a submission when it is full? - On shutdown, should queued tasks that have not started still run, or be cancelled? - Do tasks need priorities, delays or repeating schedules? ### Part 1 — Model the pool Define the public API (creating the pool, submitting work, waiting for results, shutting down) and the classes behind it: what each one owns, the state it holds, and how the pieces interact over a task's lifetime. ```hint What the submitter keeps Think about what object the caller holds after submitting, and how it gets connected to whichever worker eventually runs the task. ``` #### What This Part Should Cover - A clear API, including a handle the caller uses to obtain the result or error - Responsibilities of the pool, the task queue, the workers and the result handle - Lifecycle states of the pool and of an individual task - Configuration: worker count, queue bound and what happens to rejected work ### Part 2 — Implement the core Implement `submit`, the worker loop, the result handle and `shutdown`, with both a graceful variant that finishes queued work and one that cancels it. Explain which lock protects which state, and why your workers neither spin while idle nor miss a wake-up. ```hint Waking and re-checking Ask what a worker should do when it wakes up and finds the queue still empty, and who wakes sleeping workers when the pool shuts down. ``` #### What This Part Should Cover - Correct use of a lock and condition variable around the queue - Running the task outside the lock, with exceptions captured into the result handle - Shutdown with no lost wake-ups and no caller left waiting forever - Behavior when the queue is full or the pool has already shut down ### What a Strong Answer Covers - A clean separation between the pool, its queue, its workers and the result handles - Thread-safe code with no busy waiting, no lost wake-ups and no lock held while a task runs - Results, exceptions and cancellations always delivered to the submitter - Bounded resources and a defined policy for rejected work - Awareness of pool-specific hazards, such as tasks that wait on other tasks in the same pool - Sizing guidance for CPU-bound versus I/O-bound work ### Follow-up Questions - A task submits a second task to the same pool and waits for its result. When can that deadlock, and how do you prevent it? - How would you let the pool grow up to a maximum size under load and shrink back when idle? - How would you add delayed and repeating tasks? - How would you choose the number of workers for CPU-bound versus I/O-bound workloads?

Overview: Low-level design and concurrency question: design and implement a fixed-size thread pool with task submission, futures that return results or errors, and graceful or cancelling shutdown. It tests lock and condition variable use, lost wake-ups, policies for rejected work, pool sizing and deadlocks between dependent tasks.

Read the full Microsoft Software Engineer interview experience this question came from

|Home/Software Engineering Fundamentals/Microsoft
Microsoft logo
Microsoft
Apr 2, 2026
mediumSoftware EngineerOtherSoftware Engineering Fundamentals
0
0

Design and implement a thread pool: a fixed set of worker threads that run tasks submitted by other threads, so the application does not start a new thread for every task. Model the classes first, then implement the parts where concurrency bugs live: submitting a task, the worker loop, delivering results and errors back to the submitter, and shutting the pool down.

As in other low-level design rounds, the interviewer looks for clear class responsibilities and correct implementations of the key methods rather than every helper.

Constraints and Clarifications

  • Any thread may submit tasks at any time, including code that is itself running inside the pool.
  • The submitter must be able to wait for a task's result and must see any exception the task raised.
  • Shutdown must not leave worker threads running or callers waiting forever.
  • You may use the language's mutexes, condition variables and threads, but not its built-in executor or thread-pool classes.

Clarifying Questions Guidance

  • Is the number of workers fixed at creation, or should it grow and shrink with load?
  • Should the task queue be bounded, and what should happen to a submission when it is full?
  • On shutdown, should queued tasks that have not started still run, or be cancelled?
  • Do tasks need priorities, delays or repeating schedules?

Part 1 — Model the pool

Define the public API (creating the pool, submitting work, waiting for results, shutting down) and the classes behind it: what each one owns, the state it holds, and how the pieces interact over a task's lifetime.

What This Part Should Cover Guidance

  • A clear API, including a handle the caller uses to obtain the result or error
  • Responsibilities of the pool, the task queue, the workers and the result handle
  • Lifecycle states of the pool and of an individual task
  • Configuration: worker count, queue bound and what happens to rejected work

Part 2 — Implement the core

Implement submit, the worker loop, the result handle and shutdown, with both a graceful variant that finishes queued work and one that cancels it. Explain which lock protects which state, and why your workers neither spin while idle nor miss a wake-up.

What This Part Should Cover Guidance

  • Correct use of a lock and condition variable around the queue
  • Running the task outside the lock, with exceptions captured into the result handle
  • Shutdown with no lost wake-ups and no caller left waiting forever
  • Behavior when the queue is full or the pool has already shut down

What a Strong Answer Covers Guidance

  • A clean separation between the pool, its queue, its workers and the result handles
  • Thread-safe code with no busy waiting, no lost wake-ups and no lock held while a task runs
  • Results, exceptions and cancellations always delivered to the submitter
  • Bounded resources and a defined policy for rejected work
  • Awareness of pool-specific hazards, such as tasks that wait on other tasks in the same pool
  • Sizing guidance for CPU-bound versus I/O-bound work

Follow-up Questions Guidance

  • A task submits a second task to the same pool and waits for its result. When can that deadlock, and how do you prevent it?
  • How would you let the pool grow up to a maximum size under load and shrink back when idle?
  • How would you add delayed and repeating tasks?
  • How would you choose the number of workers for CPU-bound versus I/O-bound workloads?