Threading in Rust

 

What is Threading?

Imagine you’re a superhero with multiple clones. Instead of doing everything yourself, your clones help you tackle different tasks simultaneously. That’s threading in a nutshell – executing multiple tasks at the same time!

In Rust, threading lets your programs run faster by using multiple CPU cores efficiently. But beware!  It also brings race conditions and data synchronization issues if not handled properly. 

Creating a Simple Thread

Rust provides the std::thread module to create and manage threads.

use std::thread;
use std::time::Duration;

fn main() {
    thread::spawn(|| {
        for i in 1..=5 {
            println!("Hello from spawned thread! Count: {}", i);
            thread::sleep(Duration::from_millis(500));
        }
    });

    println!("Main thread says hi!");
    thread::sleep(Duration::from_secs(3));
}

 thread::spawn creates a new thread.sleep() makes the thread pause for a while. The main thread continues executing independently.

Waiting for Threads to Finish (Join)

By default, Rust doesn’t wait for spawned threads to finish before the main thread exits!  To prevent this, use .join().

use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..=5 {
            println!("Processing: {}", i);
        }
    });

    handle.join().unwrap(); // Ensures the thread completes before moving on!
    println!("Thread execution finished!");
}

 .join().unwrap() waits for the thread to complete before continuing.

Sending Data Between Threads

Threads don’t share state easily in Rust (thanks, borrow checker!). Instead, use message passing with channels!

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();
    
    thread::spawn(move || {
        let message = "Hello from thread!";
        tx.send(message).unwrap();
    });

    let received = rx.recv().unwrap();
    println!("Main thread received: {}", received);
}

 mpsc::channel() creates a sender (tx) and receiver (rx).send() transfers data between threads.  .recv() waits for a message to be received.

Sharing Data Between Threads

Use Arc (Atomic Reference Counting) and Mutex (Mutual Exclusion) to safely share data between threads!

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

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..5 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Final counter value: {}", *counter.lock().unwrap());
}

 Arc<T> allows safe data sharing between threads.  Mutex<T> ensures only one thread accesses data at a time.

When to Use Threading? 

 When you need to run tasks in parallel.  When you want to maximize CPU usage.  When you’re building high-performance applications.

But beware!  Threading can be tricky with shared data. Always synchronize properly to avoid data corruption!

Post a Comment

0 Comments