C++ Task Scheduler: Complete Guide
A Task Scheduler is a powerful tool that allows you to automate code execution at specific times or with a given periodicity. In C++, implementing such a scheduler requires an understanding of multithreading, synchronization, and working with time intervals. In this article, we will break down how to create your own task scheduler from scratch using the standard C++ library (C++11 and newer).
Key Components of a Scheduler
Any task scheduler consists of several key elements:
- Task Storage — a queue or container for storing scheduled tasks.
- Timer — a mechanism that determines when a task should be executed.
- Thread Pool — for parallel task execution without blocking the main thread.
- Cancellation Mechanism — the ability to cancel a task before its execution.
Simple Single-Threaded Scheduler Implementation
Let's start with a basic example where tasks are executed sequentially in a background thread:
#include <iostream>#include <thread>#include <chrono>#include <functional>#include <queue>#include <mutex>#include <condition_variable>
class TaskScheduler {public: // Schedules a task with a delay void schedule(std::function<void()> task, int delay_ms) { std::lock_guard<std::mutex> lock(mutex_); tasks_.push({std::chrono::steady_clock::now() + std::chrono::milliseconds(delay_ms), task}); cv_.notify_one(); }
// Starts the scheduler in a separate thread void start() { worker_ = std::thread([this]() { while (!stop_) { std::unique_lock<std::mutex> lock(mutex_); if (tasks_.empty()) { cv_.wait(lock, [this]() { return !tasks_.empty() || stop_; }); } if (stop_) break;
auto now = std::chrono::steady_clock::now(); auto& top = tasks_.top(); if (top.time <= now) { auto task = top.task; tasks_.pop(); lock.unlock(); task(); // Execute the task } else { cv_.wait_until(lock, top.time); } } }); }
void stop() { stop_ = true; cv_.notify_all(); if (worker_.joinable()) worker_.join(); }
private: struct Task { std::chrono::steady_clock::time_point time; std::function<void()> task; bool operator>(const Task& other) const { return time > other.time; } };
std::priority_queue<Task, std::vector<Task>, std::greater<Task>> tasks_; std::mutex mutex_; std::condition_variable cv_; std::thread worker_; bool stop_ = false;};
int main() { TaskScheduler scheduler; scheduler.start();
scheduler.schedule([]() { std::cout << "Task 1 executed after 2 seconds"; }, 2000);
scheduler.schedule([]() { std::cout << "Task 2 executed after 1 second"; }, 1000);
std::this_thread::sleep_for(std::chrono::seconds(3)); scheduler.stop(); return 0;}Scheduler with a Thread Pool for Parallel Execution
For more complex scenarios where tasks may take a long time to execute, it is better to use a thread pool. Here is an example with a minimal pool:
#include <vector>#include <queue>#include <thread>#include <mutex>#include <condition_variable>#include <f Also in library
Lottie for Swift: Complete Guide with Examples
Data Validation in TypeScript: Zod, Yup, and io-ts Libraries
Prometheus Metrics in Swift: Integration for iOS/macOS Applications
Working with Markdown in Go: libraries blackfriday, goldmark and chroma