Rust std::thread: Learn Multithreading in Rust 🎯

beginner
5 min

Rust std::thread: Learn Multithreading in Rust 🎯

Welcome to another exciting lesson at CodeYourCraft! Today, we're going to dive into the world of concurrency in Rust by exploring the std::thread module. By the end of this tutorial, you'll be able to write multi-threaded programs that can take on multiple tasks simultaneously, improving the performance of your applications.

Let's start by understanding what multithreading is. Multithreading is a programming technique that allows a single program to run multiple threads of execution concurrently. Each thread can perform different tasks or parts of a task independently, improving overall program efficiency.

Creating a Thread in Rust 💡

In Rust, we use the std::thread module to create and manage threads. Let's start by creating a new thread.

rust
use std::thread; fn main() { // Create a new thread that runs the function `my_thread_function` let handle = thread::spawn(|| { println!("Hello from a thread!"); }); // The main thread continues to run println!("Hello from the main thread!"); // The new thread is also run, and we wait for it to finish handle.join().unwrap(); }

In the code above, we first import the std::thread module. Then, we define a new thread by calling thread::spawn(), which takes a closure (an anonymous function) as an argument. Inside the closure, we print a message specific to the thread.

The main thread continues to run after creating the new thread, and we print a message from the main thread. To wait for the new thread to finish, we call handle.join(), which returns the thread handle and waits for the thread to complete.

Passing Arguments to Threads 📝

You can pass arguments to threads by creating a closure that takes arguments and using them inside the closure. Here's an example:

rust
use std::thread; fn main() { let args = vec![1, 2, 3, 4, 5]; // Create a new thread for each argument let mut handles = vec![]; for arg in args { let arg = arg; // Capture the variable by value let handle = thread::spawn(move || { println!("The thread received {}", arg); }); handles.push(handle); } // Wait for all threads to finish for handle in handles { handle.join().unwrap(); } }

In this example, we create a vector of arguments and spawn a new thread for each argument. We use the move keyword to ensure that the closure captures the variable arg by value, rather than by reference. This is essential because each thread would have its own copy of arg after the closure is moved into the new thread.

Synchronizing Threads 💡

Synchronizing threads is crucial when multiple threads need to access shared resources or communicate with each other. Rust provides several synchronization primitives, such as Mutex, Condvar, and RwLock. In this lesson, we'll focus on Mutex.

rust
use std::sync::{Mutex, Arc}; use std::thread; fn main() { // Create a shared counter wrapped in a Mutex let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; // Create 10 threads that increment the counter for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } // Wait for all threads to finish and print the final counter value for handle in handles { handle.join().unwrap(); } println!("The final counter value is: {}", *counter.lock().unwrap()); }

In this example, we create a shared counter wrapped in a Mutex to ensure safe concurrent access. We create 10 threads that increment the counter, and we use Arc::clone() to create a new Arc instance for each thread, which ensures that the Mutex is shared between threads correctly.

Quiz 📝

Quick Quiz
Question 1 of 1

What does `thread::spawn()` do in Rust?

We hope you found this tutorial informative and engaging! In the next lesson, we'll dive deeper into synchronization in Rust, exploring Condvar and RwLock. Stay tuned! 🎯