Rust- 多线程

Rust’s standard library provides support for native threads with its std::thread module, allowing you to run code in parallel. When programming in Rust, you have a high degree of control over threads, but this comes with an added layer of complexity.

Here’s an example of how to create a new thread in Rust:

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

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

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

In this example, we use the thread::spawn function to create a new thread. The code to be run in the new thread is placed inside a closure.

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

fn main() {
    /*
        创建线程 std::thread::spawn();
     */

    // Case1: 主线程结束,即使子线程没有执行完毕也要结束
    thread::spawn(|| {
        for i in 1..10 {
            println!("子线程{}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("主线程{}", i);
        thread::sleep(Duration::from_millis(1));
    }

    // thread::sleep()会让线程睡眠一段时间,某个线程睡眠的时候,让出CPU,
    // 可以让不同的线程交替执行,这要看操作系统如何调度线程

    // Case2:使用Join方法,等待子线程全部执行完毕再结束
    let handler = thread::spawn(|| {
        for i in 1..10 {
            println!("子线程{}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("主线程{}", i);
        thread::sleep(Duration::from_millis(1));
    }
    handler.join().unwrap();
}

One major aspect of multi-threading in Rust is communication between threads. Rust provides several ways for threads to communicate and synchronize with each other, such as:

  1. Channels: Channels are a way of sending data between threads. They are provided by the std::sync::mpsc module (the mpsc stands for “multiple producer, single consumer”).

  2. Mutexes: Mutexes allow us to control access to shared data by ensuring that only one thread can access the data at any one time.

  3. Arcs: The Arc type, which stands for “atomic reference count”, is a type of smart pointer that allows multiple threads to safely share ownership of some data.

Finally, Rust’s ownership system extends to its handling of threads, ensuring that data races cannot occur. Rust enforces these rules at compile time, which eliminates a whole class of potential bugs related to thread safety.

1. Channels

Channels in Rust allow two or more threads to communicate with each other by transmitting a certain type of data from one thread to another. Rust provides the std::sync::mpsc module for this purpose. Here’s an example:

use std::sync::mpsc;
use std::thread;
fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("hello");
        tx.send(val).unwrap();
    });

    let received = rx.recv().unwrap();
    println!("Got: {}", received);  // Got: hello
}

In this example, we’re creating a new channel with mpsc::channel(), then moving the transmitter end into a new thread and sending the string “hello” down the channel. The receiver end stays in the main thread and waits to receive the message.

2. Mutexes

Mutexes provide a mechanism to protect shared data. Here’s an example:

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

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

    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);
    }

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

    println!("Result: {}", *counter.lock().unwrap());  // Result: 10
}

  In this example, we’re creating a counter inside a Mutex to protect access to this shared state. We’re then creating ten threads, each of which will get a lock on the Mutex and increment the counter. Let’s analyze this program in detail:

  1. Arc::new(Mutex::new(0)) creates a new atomic reference count (Arc) around a mutex that guards an integer (i32). The Arc is used so that the counter can be shared safely among multiple threads.

  2. A for loop is used to spawn 10 threads. For each iteration:

    • A clone of the Arc containing the counter is created. Cloning an Arc increases the reference count.

    • The cloned Arc is moved into the new thread.

    • The thread acquires a lock to the mutex (with counter.lock().unwrap()), which returns a mutable reference to the value inside the mutex (i.e., the counter).

    • The thread then increments the value the mutable reference points to (*num += 1), which is the value of the counter.

    • The thread then ends, the lock is released, and the Arc’s reference count is decreased.

  3. handles.push(handle); is used to store the join handle for each thread in a vector, so that we can later wait for all threads to finish execution.

  4. After the threads have been spawned, the main thread waits for all threads to finish by calling handle.join().unwrap(); for each handle in the vector.

  5. Finally, the main thread acquires a lock on the counter one more time to print the final value. Because each thread incremented the counter exactly once, and we waited for all threads to finish before printing, the output is “Result: 10”.

So the counter ends up being 10 because the number was incremented once in each of the 10 threads.

3. Arcs

Arc stands for Atomic Reference Counting. It’s a thread-safe reference-counting pointer, used when you need to share data between multiple threads. In the previous Mutex example, we used an Arc to share the Mutex between multiple threads.

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

fn main() {
    let data = Arc::new(Mutex::new(vec![1, 2, 3]));

    for _ in 0..3 {
        let data = Arc::clone(&data);
        thread::spawn(move || {
            let mut data = data.lock().unwrap();
            data[0] += 1;
        });
    }
}

In this example, we’re sharing a Mutex<Vec<u32>> among multiple threads. Without the Arc, Rust’s ownership rules would prevent us from moving the Mutex into more than one thread.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青衫客36

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值