Modern C++ thread-pool scheduler that distributes work across a fixed set of std::jthread workers. The goal is to demonstrate a clear, dependency-free approach to concurrent task execution that is easy to study and embed in other projects.
The scheduler exposes a minimal API. Construct it with the desired number of worker threads and call submit() with any callable. Pending tasks live in a mutex-protected queue, while workers block on a condition variable until work arrives or shutdown is requested. The design is compact enough for teaching and prototyping but still follows best practices for synchronization and RAII-based shutdown.
- Simplicity: Keep the API and implementation small enough to reason about at a glance.
- Correctness: Rely solely on standard C++ synchronization primitives.
- Safety: Ensure threads shut down cleanly without manual joins.
- Modern C++: Highlight how
std::jthread, atomics, and condition variables fit together.
- Task queue –
std::queue<std::function<void()>>guarded by astd::mutex. - Worker thread pool – fixed number of
std::jthreadinstances, each running the same worker loop. - Synchronization primitives – mutex +
std::condition_variablefor coordination,std::atomic<bool>for the shutdown flag.
- Reserve storage for all workers on construction.
- Spawn
num_threadsworkers immediately; each starts waiting on the condition variable for incoming tasks.
submit(F&&)acquires the queue mutex, pushes the callable via perfect forwarding, and notifies one worker.- Only a single worker wakes up, preventing unnecessary context switches.
- Lock the queue using
std::unique_lock. - Wait until either a task is present or shutdown is signaled.
- If shutdown is true and the queue is empty, exit the loop.
- Otherwise, pop the next task, release the lock, and execute the callable.
- Repeat until shutdown completes.
Releasing the lock before running user code keeps other workers from blocking on long-running tasks.
shutdown()sets the atomic flag while holding the mutex, thennotify_all()to wake every worker.- Each worker drains remaining tasks before exiting.
- The destructor calls
shutdown()so the scheduler always terminates cleanly, andstd::jthreadhandles joining automatically.
- Modern C++23 implementation (
std::jthread, atomics, condition variables). - Thread-safe task queue with efficient worker wake-ups.
- Template-based
submit()accepts lambdas, function objects, or function pointers. - Graceful shutdown that drains outstanding work.
- Zero external dependencies. Pure standard library.
- C++23-capable compiler (GCC 13+, Clang 17+, MSVC 17.6+).
- POSIX threads support (on Linux/macOS). Windows builds rely on the MSVC threading runtime.
g++ -std=c++23 -pthread src/*.cpp -o scheduler-demo./scheduler-demoThe sample main spins up four worker threads, enqueues ten tasks, and pauses long enough for everything to finish.
#include "scheduler.hpp"
#include <iostream>
#include <thread>
#include <chrono>
int main() {
Scheduler scheduler(4);
for (int i = 0; i < 10; ++i) {
scheduler.submit([i]() {
std::cout << "Task " << i << " executed by thread\n";
std::this_thread::sleep_for(std::chrono::milliseconds(100));
});
}
std::this_thread::sleep_for(std::chrono::seconds(2));
}Task 0 executed by thread
Task 1 executed by thread
Task 2 executed by thread
Task 3 executed by thread
Task 4 executed by thread
...
src/scheduler.hpp– Scheduler class definition and templatedsubmit.src/scheduler.cpp– Constructor, worker loop, shutdown logic.src/main.cpp– Reference program illustrating setup, submission, and teardown.