w3resource

Rust Concurrent Task Scheduler with Threads

Rust Threads and Synchronization: Exercise-6 with Solution

Write a Rust program that develops a concurrent task schedule that executes tasks using multiple threads.

Sample Solution:

Rust Code:

use std::sync::{Arc, Mutex};
use std::thread;

// Define a type for tasks
type Task = Box<dyn FnOnce() + Send + 'static>;

// Define a structure for the task scheduler
struct TaskScheduler {
    tasks: Arc<Mutex<Vec<Task>>>,
    threads: Vec<thread::JoinHandle<()>>,
}

impl TaskScheduler {
    // Create a new instance of the task scheduler
    fn new() -> Self {
        TaskScheduler {
            tasks: Arc::new(Mutex::new(Vec::new())),
            threads: Vec::new(),
        }
    }

    // Add a task to the scheduler
    fn add_task(&self, task: Task) {
        self.tasks.lock().unwrap().push(task);
    }

    // Start the task scheduler with a specified number of threads
    fn start(&mut self, num_threads: usize) {
        for _ in 0..num_threads {
            let tasks = Arc::clone(&self.tasks);
            let handle = thread::spawn(move || {
                loop {
                    // Lock the tasks vector
                    let task = tasks.lock().unwrap().pop();

                    // Execute the task if it exists
                    if let Some(task) = task {
                        task();
                    } else {
                        // If no task is available, break the loop
                        break;
                    }
                }
            });
            self.threads.push(handle);
        }
    }

    // Wait for all threads to finish executing
    fn wait_all(&mut self) {
        for handle in self.threads.drain(..) {
            handle.join().unwrap();
        }
    }
}

fn main() {
    // Create a new task scheduler
    let mut scheduler = TaskScheduler::new();

    // Add tasks to the scheduler
    for i in 0..10 {
        let task = Box::new(move || {
            println!("Task {} executed by thread {:?}", i, thread::current().id());
        });
        scheduler.add_task(task);
    }

    // Start the task scheduler with 4 threads
    scheduler.start(4);

    // Wait for all threads to finish executing
    scheduler.wait_all();
}

Output:

Task 9 executed by thread ThreadId(2)
Task 8 executed by thread ThreadId(5)
Task 5 executed by thread ThreadId(5)
Task 4 executed by thread ThreadId(5)
Task 3 executed by thread ThreadId(5)
Task 2 executed by thread ThreadId(5)
Task 1 executed by thread ThreadId(5)
Task 0 executed by thread ThreadId(5)
Task 7 executed by thread ThreadId(4)
Task 6 executed by thread ThreadId(3)

Explanation:

In the exercise above,

  • Define a Task type alias, representing a closure that takes no arguments and returns nothing (FnOnce() + Send + 'static). This type can be executed in parallel and sent between threads.
  • Define a "TaskScheduler" structure to manage the execution of tasks. It contains a vector of tasks wrapped in a mutex to allow multiple threads to access it safely, as well as a vector of thread handles to keep track of spawned threads.
  • The "TaskScheduler" has methods to add tasks (add_task), start executing tasks with a specified number of threads (start), and wait for all threads to finish executing (wait_all).
  • In the "main()" function, we create a new "TaskScheduler" instance and add some tasks to it.
  • We start the task scheduler with 4 threads using the "start()" method.
  • Finally, we wait for all threads to finish executing using the "wait_all()" method

Rust Code Editor:

Previous: Rust Concurrent Web Server with Threads.
Next: Rust Parallel Merge Sort Algorithm with Threads.

What is the difficulty level of this exercise?

Test your Programming skills with w3resource's quiz.



Follow us on Facebook and Twitter for latest update.