Skip to content

Latest commit

 

History

History
115 lines (80 loc) · 2.72 KB

File metadata and controls

115 lines (80 loc) · 2.72 KB

Multithreading

In most current operating systems, an executed program's code is run in a process, and the OS can manage multiple processes at once.

Within a program, you can also have independent parts that run simultaneously. These independent parts are called threads.

For example, a web server could have multiple threads to handle multiple incoming requests concurrently.


Basic Thread Creation

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

fn main() {
    thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {i} from the spawned thread!");
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("hi number {i} from the main thread");
        thread::sleep(Duration::from_millis(1));
    }
}

This code creates a new thread using thread::spawn, which runs in parallel with the main thread. You may not always see all output from the spawned thread, as the main thread may finish first.


Waiting for the Thread to Finish

You can make the main thread wait for the spawned thread using .join():

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

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {i} from the spawned thread!");
            thread::sleep(Duration::from_millis(1));
        }
    });

    handle.join().unwrap(); // waits for the spawned thread to finish

    for i in 1..5 {
        println!("hi number {i} from the main thread");
        thread::sleep(Duration::from_millis(1));
    }
}

Using .join() ensures that the spawned thread completes before the main thread continues.


Using move Closures with Threads

We'll often use the move keyword with closures passed to thread::spawn because:

  • Threads can outlive their spawning function.
  • Variables captured in closures must be owned if they are moved into another thread.
  • move transfers ownership into the thread.

❌ This code won't compile:

use std::thread;

fn main() {
    let v = vec![1, 2, 3];

    let handle = thread::spawn(|| {
        // ❗ Error: closure may outlive the current function and `v` doesn't live long enough
        println!("Here's a vector: {:?}", v);
    });

    handle.join().unwrap();
}

Rust prevents this to avoid using data after it’s dropped.


✅ Fix using move:

use std::thread;

fn main() {
    let v = vec![1, 2, 3];

    let handle = thread::spawn(move || {
        // `v` is now owned by the thread
        println!("Here's a vector: {:?}", v);
    });

    handle.join().unwrap();
}

Now v is moved into the thread, and the compiler is happy. Remember: once moved, v can no longer be used in the main thread.