Simple, general-purpose thread pool.
This is a straightforward thread pool implementation suitable for:
- Simple workloads with low task counts (< 1k tasks)
- General application use (50k-500k tasks/second)
- Simple task submission patterns
- Lower memory overhead and complexity
- Easier to understand and debug
For high-throughput scenarios (> 1k tasks), consider FastThreadPool or HighPerformancePool.
- How task execution works
- When you call submit(), the callable is wrapped in a std::packaged_task and pushed into a single shared std::queue under a mutex lock. One sleeping worker is then woken via condition_variable::notify_one(). The woken worker pops the front task from the queue and executes it. Workers block indefinitely on the condition_variable when the queue is empty (no polling timeout), so they consume zero CPU while idle.
- Execution guarantees
- Every successfully submitted task (submit() returned without throwing) is guaranteed to eventually execute.
- submit() throws std::runtime_error if the pool is already shutting down. In that case the task is NOT enqueued.
- Tasks are stored in a FIFO queue. Multiple workers pop concurrently, so submission order is roughly preserved but completion order is non-deterministic.
- The returned std::future becomes ready once the task finishes. If the task threw an exception, future.get() rethrows it.
- On shutdown(), the stop flag is set and all workers are woken. Each worker finishes its current task and then exits only if the queue is empty. This means all tasks that were enqueued before shutdown() are guaranteed to execute.
- wait_for_tasks() blocks until the queue is empty AND no worker is currently executing a task.
- Thread safety
- submit() may be called from any thread concurrently. All task-queue access is serialized through queue_mutex_.
- Wake-up behaviour
- Workers block on a std::condition_variable (no polling timeout), so they consume no CPU while idle but wake instantly when a task is enqueued.
- Internal counter note
- Unlike FastThreadPool and HighPerformancePool, active_tasks_ and completed_tasks_ are incremented/decremented while queue_mutex_ is held. This means they are always consistent with the queue size, but every task completion acquires the mutex an extra time.
- Exception handling
- Exceptions thrown by tasks are caught inside the worker loop. They are stored in the std::future returned by submit(). The worker thread continues processing.
- Lifetime
- The destructor calls shutdown() and joins all worker threads. Can block if tasks are still running.
- Copyability / movability
- Not copyable, not movable.
Definition at line 1105 of file thread_pool.hpp.