[rust]多线程的基本使用

42 阅读3分钟

创建多线程

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

fn main() {
    println!("{:?}, main begin", thread::current().id()); // 获取当前执行线程的线程号(Thread ID)
    let handle1 = thread::spawn(|| {
        for i in 1..4 {
            println!("{:?}, child println num: {}", thread::current().id(), i);
            thread::sleep(Duration::from_millis(1)) // 休眠1ms
        }
    }); // 线程可以有返回值
    let handle2 = thread::spawn(|| {
        for i in 1..=2 {
            println!("{:?}, child println num: {}", thread::current().id(), i);
            thread::sleep(Duration::from_millis(1))
        }
    });
    handle1.join().unwrap(); // 等待子线程的结束才会往下执行
    handle2.join().unwrap();

    println!("{:?}, main end", thread::current().id()); // 获取当前执行线程的线程号(Thread ID)
}

// 输出:
// ThreadId(1), main begin
// ThreadId(2), child println num: 1
// ThreadId(3), child println num: 1
// ThreadId(2), child println num: 2
// ThreadId(3), child println num: 2
// ThreadId(2), child println num: 3
// ThreadId(1), main end
// 
// 进程已结束,退出代码为 0

线程与move闭包

错误示例

fn main() {
    let v = vec![1,2,3];
   thread::spawn(||{
       println!("v: {:?}",v)
   });

    drop(v);
}

执行报错

error[E0373]: closure may outlive the current function, but it borrows `v`, which is owned by the current function
   --> src/main.rs:124:18
    |
124 |    thread::spawn(||{
    |                  ^^ may outlive borrowed value `v`
125 |        println!("v: {:?}",v)
    |                           - `v` is borrowed here
    |
note: function requires argument type to outlive `'static`
   --> src/main.rs:124:4
    |
124 | /    thread::spawn(||{
125 | |        println!("v: {:?}",v)
126 | |    });
    | |_____^
help: to force the closure to take ownership of `v` (and any other referenced variables), use the `move` keyword
    |
124 |    thread::spawn(move ||{
    |                  ++++

原因分析:变量v在子线程中被使用时,子线程并不知道该变量的生命周期有多长,所以无法保证变量v在使用期间始终是有效的,为了安全起见,直接报错。

使用move

move关键字通常用于闭包中,以明确将闭包环境中的变量所有权转移到闭包内

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

fn main() {
    let v = vec![1, 2, 3];
    let handle = thread::spawn(move || {
        // // `v`的所有权被移动到了闭包内
        println!("v: {:?}", v)
    });

    // 此处`v`的所有权已经转移,不能再使用`v`
    // println!("v: {:?}",v); 如果启用这一行会导致编译错误
    handle.join().unwrap();
}

创建线程池

Rust标准库(std)本身不包含直接用于创建线程池的功能,为了使用线程池,推荐使用第三方库,如rayonthreadpool

threadpool

threadpool是一个轻量级线程池库,提供了基本的线程池功能

在Cargo.toml中添加依赖

[dependencies]
threadpool = "1.8.1"

示例:

use std::thread;
use std::time::Duration;
use threadpool::ThreadPool;

fn main() {
    println!("{:?}-main thread begin", thread::current().id());

    // 创建一个包含4个线程的线程池
    let pool = ThreadPool::new(4);

    // 向线程池提交任务
    for i in 1..=8 {
        pool.execute(move || {
            println!("{:?}-Processing task {}", thread::current().id(), i);
            thread::sleep(Duration::from_secs(1)); // 休眠1秒,模拟一些工作
            println!("{:?}-Task {} completed", thread::current().id(), i);
        });
    }

    // 等待所有任务完成
    pool.join();

    println!("{:?}-main thread end", thread::current().id());
}

// 输出:
// ThreadId(1)-main thread begin
// ThreadId(2)-Processing task 1
// ThreadId(3)-Processing task 2
// ThreadId(5)-Processing task 3
// ThreadId(4)-Processing task 4
// ThreadId(2)-Task 1 completed
// ThreadId(2)-Processing task 5
// ThreadId(3)-Task 2 completed
// ThreadId(3)-Processing task 6
// ThreadId(5)-Task 3 completed
// ThreadId(5)-Processing task 7
// ThreadId(4)-Task 4 completed
// ThreadId(4)-Processing task 8
// ThreadId(2)-Task 5 completed
// ThreadId(3)-Task 6 completed
// ThreadId(5)-Task 7 completed
// ThreadId(4)-Task 8 completed
// ThreadId(1)-main thread end
// 
// 进程已结束,退出代码为 0

主线程的ID为1,其他子线程ID从2开始分配,线程池中有四个线程,即ID为2~5

rayon

rayon是一个数据并行ism库,非常适合高性能并行处理

[dependencies]
rayon = "1.10.0"

示例:

fn main() {
    println!("{:?}-main thread begin", thread::current().id());

    let v: Vec<i32> = (0..=100).collect();
    let sum: i32 = v.par_iter().sum(); // 并行化迭代和求和
    println!("Sum of 0..101 is: {}", sum);

    println!("{:?}-main thread end", thread::current().id());
}

// 输出:
// ThreadId(1)-main thread begin
// Sum of 0..101 is: 5050
// ThreadId(1)-main thread end