Rust多线程编程学习笔记

目录

  1. [Rust 多线程基础](#Rust 多线程基础)
  2. 同步线程编程
  3. 异步线程编程
  4. 线程安全
  5. 性能优化
  6. 最佳实践
  7. 完整代码示例
  8. 总结

Rust 多线程基础

Rust 的多线程编程建立在标准库的 std::thread 模块之上。与其他语言不同,Rust 通过其所有权系统和类型系统来保证线程安全,避免了常见的并发问题如数据竞争。

核心概念

  • 线程:操作系统级别的执行单元
  • 所有权:Rust 的核心概念,确保每个值只有一个所有者
  • 借用:临时访问他人拥有的值
  • 同步原语 :如 MutexArc 等,用于线程间协调
  • 通道:用于线程间安全通信

同步线程编程

基本线程创建

使用 thread::spawn 函数创建新线程:

rust 复制代码
use std::thread;
use std::time::Duration;

fn main() {
    println!("主线程开始");
    
    // 创建新线程
    let handle = thread::spawn(|| {
        for i in 1..=5 {
            println!("子线程: {}", i);
            thread::sleep(Duration::from_millis(100));
        }
    });
    
    // 主线程继续执行
    for i in 1..=3 {
        println!("主线程: {}", i);
        thread::sleep(Duration::from_millis(150));
    }
    
    // 等待子线程完成
    handle.join().unwrap();
    
    println!("主线程结束");
}

运行结果

复制代码
主线程开始
主线程: 1
子线程: 1
子线程: 2
主线程: 2
子线程: 3
子线程: 4
主线程: 3
子线程: 5
主线程结束

线程间通信

使用通道(channel)实现线程间通信:

rust 复制代码
use std::thread;
use std::sync::mpsc;
use std::time::Duration;

fn main() {
    // 创建通道
    let (tx, rx) = mpsc::channel();
    
    // 发送线程
    thread::spawn(move || {
        let messages = ["Hello", "from", "the", "spawned", "thread"];
        for msg in messages {
            tx.send(msg).unwrap();
            thread::sleep(Duration::from_millis(200));
        }
    });
    
    // 接收线程(主线程)
    println!("等待接收消息...");
    for received in rx {
        println!("接收到: {}", received);
    }
    println!("所有消息接收完毕");
}

运行结果

复制代码
等待接收消息...
接收到: Hello
接收到: from
接收到: the
接收到: spawned
接收到: thread
所有消息接收完毕

共享状态

使用 ArcMutex 实现线程安全的共享状态:

rust 复制代码
use std::thread;
use std::sync::{Arc, Mutex};
use std::time::Duration;

fn main() {
    // 创建共享计数器
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    
    // 创建 5 个线程
    for i in 0..5 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            for _ in 0..10 {
                let mut num = counter.lock().unwrap();
                *num += 1;
                println!("线程 {}: 计数器 = {}", i, *num);
                thread::sleep(Duration::from_millis(50));
            }
        });
        handles.push(handle);
    }
    
    // 等待所有线程完成
    for handle in handles {
        handle.join().unwrap();
    }
    
    println!("最终计数器值: {}", *counter.lock().unwrap());
}

运行结果

复制代码
线程 0: 计数器 = 1
线程 0: 计数器 = 2
...
线程 4: 计数器 = 50
最终计数器值: 50

线程返回值

从线程中返回计算结果:

rust 复制代码
use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        println!("子线程开始计算");
        let mut sum = 0;
        for i in 1..=100 {
            sum += i;
        }
        println!("子线程计算完成");
        sum // 返回值
    });
    
    println!("主线程等待结果...");
    let result = handle.join().unwrap();
    println!("1 到 100 的和: {}", result);
}

运行结果

复制代码
主线程等待结果...
子线程开始计算
子线程计算完成
1 到 100 的和: 5050

线程池

创建简单的线程池处理任务:

rust 复制代码
use std::thread;
use std::time::Duration;

fn main() {
    let jobs = vec![1, 2, 3, 4, 5];
    let mut handles = vec![];
    
    println!("开始处理任务...");
    for job in jobs {
        let handle = thread::spawn(move || {
            println!("处理任务 {}", job);
            thread::sleep(Duration::from_millis(300));
            println!("任务 {} 完成", job);
            job * 2 // 返回处理结果
        });
        handles.push(handle);
    }
    
    // 收集所有线程的结果
    let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
    println!("所有任务完成。结果: {:?}", results);
}

运行结果

复制代码
开始处理任务...
处理任务 1
处理任务 2
处理任务 3
处理任务 4
处理任务 5
任务 1 完成
任务 2 完成
任务 3 完成
任务 4 完成
任务 5 完成
所有任务完成。结果: [2, 4, 6, 8, 10]

异步线程编程

Tokio 异步运行时

Rust 的异步编程需要一个异步运行时,最常用的是 Tokio:

添加依赖

toml 复制代码
[dependencies]
tokio = { version = "1.36.0", features = ["full"] }

异步任务

创建和管理异步任务:

rust 复制代码
use tokio;

#[tokio::main]
async fn main() {
    println!("异步主线程开始");
    
    // 创建异步任务
    let task1 = tokio::spawn(async {
        println!("异步任务 1 开始");
        tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
        println!("异步任务 1 完成");
        100
    });
    
    let task2 = tokio::spawn(async {
        println!("异步任务 2 开始");
        tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
        println!("异步任务 2 完成");
        200
    });
    
    // 等待所有异步任务完成
    let (result1, result2) = tokio::join!(task1, task2);
    println!("任务结果: {}, {}", result1.unwrap(), result2.unwrap());
    
    println!("异步主线程结束");
}

运行结果

复制代码
异步主线程开始
异步任务 1 开始
异步任务 2 开始
异步任务 2 完成
异步任务 1 完成
任务结果: 100, 200
异步主线程结束

异步通道

异步线程间的消息传递:

rust 复制代码
use tokio;
use tokio::sync::mpsc;

#[tokio::main]
async fn main() {
    // 创建异步通道
    let (tx, mut rx) = mpsc::channel(32);
    
    // 发送任务
    tokio::spawn(async move {
        for i in 1..=5 {
            tx.send(i).await.unwrap();
            println!("发送: {}", i);
            tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
        }
    });
    
    // 接收任务
    println!("等待接收消息...");
    while let Some(msg) = rx.recv().await {
        println!("接收: {}", msg);
    }
    println!("所有消息接收完毕");
}

运行结果

复制代码
等待接收消息...
发送: 1
接收: 1
发送: 2
接收: 2
发送: 3
接收: 3
发送: 4
接收: 4
发送: 5
接收: 5
所有消息接收完毕

异步共享状态

使用 Tokio 的 Mutex 实现异步共享状态:

rust 复制代码
use tokio;
use std::sync::Arc;
use tokio::sync::Mutex;

#[tokio::main]
async fn main() {
    // 创建异步共享计数器
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];
    
    // 创建 3 个异步任务
    for i in 0..3 {
        let counter = Arc::clone(&counter);
        let handle = tokio::spawn(async move {
            for _ in 0..5 {
                let mut num = counter.lock().await;
                *num += 1;
                println!("异步任务 {}: 计数器 = {}", i, *num);
                tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
            }
        });
        handles.push(handle);
    }
    
    // 等待所有异步任务完成
    for handle in handles {
        handle.await.unwrap();
    }
    
    println!("最终计数器值: {}", *counter.lock().await);
}

运行结果

复制代码
异步任务 0: 计数器 = 1
异步任务 1: 计数器 = 2
异步任务 2: 计数器 = 3
异步任务 0: 计数器 = 4
异步任务 1: 计数器 = 5
异步任务 2: 计数器 = 6
异步任务 0: 计数器 = 7
异步任务 1: 计数器 = 8
异步任务 2: 计数器 = 9
异步任务 0: 计数器 = 10
异步任务 1: 计数器 = 11
异步任务 2: 计数器 = 12
异步任务 0: 计数器 = 13
异步任务 1: 计数器 = 14
异步任务 2: 计数器 = 15
最终计数器值: 15

线程安全

所有权与借用

Rust 的所有权系统是线程安全的基础:

  • 所有权:每个值只能有一个所有者
  • 借用:可以有多个不可变借用或一个可变借用
  • 生命周期:确保引用在所有者有效的期间内有效

同步原语

Rust 提供了多种同步原语:

  • Mutex:互斥锁,确保同一时间只有一个线程可以访问数据
  • RwLock:读写锁,允许多个读操作或一个写操作
  • Arc:原子引用计数,用于在多个线程间共享所有权
  • Condvar:条件变量,用于线程间的信号通知
  • Barrier:屏障,用于同步多个线程的执行

Send 和 Sync trait

  • Send:标记类型可以安全地在线程间转移所有权
  • Sync:标记类型可以安全地在线程间共享引用

Rust 的大多数类型都自动实现了这些 trait,但有些类型(如 RcRefCell)没有实现,因为它们不是线程安全的。

性能优化

线程数量

  • CPU 密集型任务:线程数通常设置为 CPU 核心数
  • I/O 密集型任务:线程数可以大于 CPU 核心数
  • 异步任务:可以创建大量轻量级的异步任务

避免竞争

  • 减少锁的范围:只在必要时持有锁
  • 使用无锁数据结构 :如 AtomicUsize
  • 使用通道:优先使用通道进行线程间通信,而不是共享状态

异步 vs 同步

  • 同步线程:适合 CPU 密集型任务,每个线程对应一个操作系统线程
  • 异步任务:适合 I/O 密集型任务,多个任务可以在同一个操作系统线程上运行

最佳实践

  1. 优先使用通道:通过消息传递进行线程间通信
  2. 最小化共享状态:减少线程间的依赖
  3. 使用 Arc<Mutex>:需要共享可变状态时的标准做法
  4. 异步编程:I/O 密集型任务优先使用异步
  5. 错误处理:妥善处理线程中的错误
  6. 资源管理:确保线程正确释放资源

完整代码示例

以下是一个完整的 Rust 多线程示例,包含了本文介绍的所有功能:

rust 复制代码
use std::thread;
use std::time::Duration;
use std::sync::{mpsc, Arc, Mutex};
use tokio;

// 基本线程示例
struct ThreadData {
    name: String,
}

impl ThreadData {
    fn new(name: String) -> Self {
        println!("ThreadData created with name: {}", name);
        ThreadData { name }
    }

    fn thread_function(&self) {
        for i in 1..=5 {
            println!("[Basic Thread] thread name: {} spawned thread: {}", self.name, i);
            thread::sleep(Duration::from_millis(100));
        }
    }
}

// 线程间通信示例
fn channel_example() {
    println!("\n=== Channel Example ===");
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let messages = ["Hello", "from", "the", "spawned", "thread"];
        for msg in messages {
            tx.send(msg).unwrap();
            thread::sleep(Duration::from_millis(200));
        }
    });

    for received in rx {
        println!("[Channel] Received: {}", received);
    }
}

// 共享状态示例
fn shared_state_example() {
    println!("\n=== Shared State Example ===");
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for i in 0..5 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            for _ in 0..10 {
                let mut num = counter.lock().unwrap();
                *num += 1;
                println!("[Shared State] Thread {}: Counter = {}", i, *num);
                thread::sleep(Duration::from_millis(50));
            }
        });
        handles.push(handle);
    }

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

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

// 线程返回值示例
fn thread_return_example() {
    println!("\n=== Thread Return Example ===");
    let handle = thread::spawn(move || {
        let mut sum = 0;
        for i in 1..=100 {
            sum += i;
        }
        sum
    });

    let result = handle.join().unwrap();
    println!("[Thread Return] Sum from 1 to 100: {}", result);
}

// 线程池示例
fn thread_pool_example() {
    println!("\n=== Thread Pool Example ===");
    let jobs = vec![1, 2, 3, 4, 5];
    let mut handles = vec![];

    for job in jobs {
        let handle = thread::spawn(move || {
            println!("[Thread Pool] Processing job {}", job);
            thread::sleep(Duration::from_millis(300));
            println!("[Thread Pool] Job {} completed", job);
            job * 2
        });
        handles.push(handle);
    }

    let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
    println!("[Thread Pool] All jobs completed. Results: {:?}", results);
}

// 异步线程示例
async fn async_example() {
    println!("\n=== Async Thread Example ===");
    
    let task1 = tokio::spawn(async {
        println!("[Async] Task 1 started");
        tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
        println!("[Async] Task 1 completed");
        100
    });
    
    let task2 = tokio::spawn(async {
        println!("[Async] Task 2 started");
        tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
        println!("[Async] Task 2 completed");
        200
    });
    
    let (result1, result2) = tokio::join!(task1, task2);
    println!("[Async] Task results: {}, {}", result1.unwrap(), result2.unwrap());
    
    // 异步通道示例
    println!("\n=== Async Channel Example ===");
    let (tx, mut rx) = tokio::sync::mpsc::channel(32);
    
    tokio::spawn(async move {
        for i in 1..=5 {
            tx.send(i).await.unwrap();
            println!("[Async Channel] Sent: {}", i);
            tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
        }
    });
    
    while let Some(msg) = rx.recv().await {
        println!("[Async Channel] Received: {}", msg);
    }
    
    // 异步共享状态示例
    println!("\n=== Async Shared State Example ===");
    let counter = Arc::new(tokio::sync::Mutex::new(0));
    let mut handles = vec![];
    
    for i in 0..3 {
        let counter = Arc::clone(&counter);
        let handle = tokio::spawn(async move {
            for _ in 0..5 {
                let mut num = counter.lock().await;
                *num += 1;
                println!("[Async Shared] Task {}: Counter = {}", i, *num);
                tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
            }
        });
        handles.push(handle);
    }
    
    for handle in handles {
        handle.await.unwrap();
    }
    
    println!("[Async Shared] Final counter value: {}", *counter.lock().await);
}

fn main() {
    println!("=== Rust Multi-threading Examples ===");

    // 1. 基本线程示例
    println!("\n=== Basic Thread Example ===");
    let x = 10;

    println!("Creating ThreadData instances...");
    let thread_data1 = ThreadData::new("Thread 1".to_string());
    let thread_data2 = ThreadData::new("Thread 2".to_string());

    println!("Spawning threads...");
    let handler = thread::spawn(move || {
        thread_data1.thread_function();
    });

    let handler2 = thread::spawn(move || {
        thread_data2.thread_function();
    });

    println!("x = {}", x);
    handler.join().unwrap();
    handler2.join().unwrap();

    // 2. 线程间通信示例
    channel_example();

    // 3. 共享状态示例
    shared_state_example();

    // 4. 线程返回值示例
    thread_return_example();

    // 5. 线程池示例
    thread_pool_example();

    // 6. 异步线程示例
    println!("\n=== Running Async Examples ===");
    tokio::runtime::Builder::new_multi_thread()
        .worker_threads(4)
        .enable_all()
        .build()
        .unwrap()
        .block_on(async_example());

    println!("\n=== All Examples Complete! ===");
}

总结

Rust 提供了强大而安全的多线程编程支持,通过其所有权系统和类型安全特性,使得多线程编程更加可靠。本文介绍了 Rust 中的各种线程使用方法,包括:

  • 同步线程:基本线程创建、线程间通信、共享状态、线程返回值和线程池
  • 异步线程:基于 Tokio 的异步任务、异步通道和异步共享状态
  • 线程安全:所有权系统、同步原语和 Send/Sync trait
  • 性能优化:线程数量选择、避免竞争和异步编程

通过本文的学习,你应该能够掌握 Rust 多线程编程的核心概念和实践技巧,编写安全、高效的并发程序。

Rust 的多线程编程虽然有一定的学习曲线,但其带来的安全性和性能优势是值得的。随着经验的积累,你会发现 Rust 的多线程编程变得越来越自然和直观。

希望本文对你有所帮助,祝你在 Rust 多线程编程的道路上越走越远!

相关推荐
中屹指纹浏览器2 小时前
进程级沙箱隔离与WebGL指纹抗识别:指纹浏览器核心技术难点与工程落地
经验分享·笔记
进阶的猪2 小时前
Qt学习笔记
笔记·学习
mango_mangojuice2 小时前
Linux学习笔记 1.19
linux·服务器·数据库·笔记·学习
Leekwen2 小时前
生命的选题
学习·思考·生活·认知高度·认知带宽
xhbaitxl2 小时前
算法学习day31-贪心算法
学习·算法·贪心算法
进阶小白猿3 小时前
Java技术八股学习Day29
学习
闫记康3 小时前
linux配置ssh
linux·运维·服务器·学习·ssh
BlackWolfSky3 小时前
鸿蒙中级课程笔记6—使用ArkWeb开发
笔记·华为·harmonyos
浅念-3 小时前
C语言——双向链表
c语言·数据结构·c++·笔记·学习·算法·链表