Task Scheduler in Rust: From a Simple Timer to an Industrial Job Scheduler
A job scheduler is one of the key components of any serious application. In Rust, you can build a system that executes with millisecond precision, consumes minimal memory, and runs without failures for years. In this article, we will break down how to create a task scheduler in Rust, using both standard tools and popular crates.
Why Write a Scheduler in Rust?
Rust offers a unique combination of performance and safety. A task scheduler in Rust:
- Guarantees the absence of data races — the compiler checks this at compile time.
- Runs without a garbage collector — predictable task execution latency.
- Supports both asynchronous and multithreaded execution.
- Has a minimal memory footprint — you can run thousands of tasks.
Simplest Scheduler with std::thread::sleep
Let's start with a basic example — cyclic execution of a task every N seconds:
use std::thread;use std::time::Duration;
fn main() { let interval = Duration::from_secs(5); loop { println!("[{}] Executing task...", chrono::Local::now().format("%H:%M:%S")); // Your task code here thread::sleep(interval); }}This approach is simple but inefficient: it blocks the current thread, and there is no way to run multiple tasks in parallel.
Async Scheduler with tokio
For real projects, use tokio — an async runtime that allows you to run thousands of tasks in a single thread.
use tokio::time::{interval, Duration};
#[tokio::main]async fn main() { let mut timer = interval(Duration::from_secs(10)); println!("Scheduler started. The task will run every 10 seconds."); loop { timer.tick().await; println!("[{}] Executing task...", chrono::Local::now().format("%H:%M:%S")); // Async task: for example, an API request do_work().await; }}
async fn do_work() { // Simulating useful work tokio::time::sleep(Duration::from_millis(500)).await; println!("Task completed.");}Advantages: does not block the thread, you can run hundreds of such schedulers in parallel.
CRON-like Scheduling with the cron Crate
If you need to define complex schedules (like in Linux cron), use the cron crate:
use cron::Schedule;use chrono::Utc;use std::str::FromStr;
fn main() { // Every minute at 30 seconds let expression = "30 * * * * *"; let schedule = Schedule::from_str(expression).unwrap(); println!("Next 5 runs:"); for datetime in schedule.upcoming(Utc).take(5) { println!(" {}", datetime); }}Here is a more complete example with actual execution:
use cron::Schedule;use chrono::{Utc, Local};use std::thread;use std::time::Duration;use std::str::FromStr;
fn main() { // Every 15 seconds let expression = "0/15 * * * * *"; let schedule = Schedule::from_str(expression).unwrap(); loop { let now = Utc::now(); if let Some(next) = schedule.upcoming(Utc).next() { let delay = next - now; let millis = delay.num_milliseconds().max(0) as u64; thread::sleep(Duration::from_millis(millis)); println!("[{}] Scheduled task!", Local::now().format("%H:%M:%S")); // Task execution } else { thread::sleep(Duration::from_secs(1)); } }}Industrial Scheduler with Multithreading
For serious projects, create a Scheduler struct