惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tor Project blog
T
The Blog of Author Tim Ferriss
WordPress大学
WordPress大学
H
Help Net Security
酷 壳 – CoolShell
酷 壳 – CoolShell
宝玉的分享
宝玉的分享
博客园 - 司徒正美
Jina AI
Jina AI
雷峰网
雷峰网
博客园_首页
博客园 - 三生石上(FineUI控件)
G
Google Developers Blog
MongoDB | Blog
MongoDB | Blog
Hugging Face - Blog
Hugging Face - Blog
The Cloudflare Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Forbes - Security
Forbes - Security
T
Threatpost
V
Visual Studio Blog
A
Arctic Wolf
Microsoft Azure Blog
Microsoft Azure Blog
Security Latest
Security Latest
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
P
Privacy & Cybersecurity Law Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Spread Privacy
Spread Privacy
N
News and Events Feed by Topic
Vercel News
Vercel News
爱范儿
爱范儿
The GitHub Blog
The GitHub Blog
NISL@THU
NISL@THU
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
PCI Perspectives
PCI Perspectives
博客园 - 【当耐特】
IT之家
IT之家
S
Secure Thoughts
Engineering at Meta
Engineering at Meta
阮一峰的网络日志
阮一峰的网络日志
Cisco Talos Blog
Cisco Talos Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
月光博客
月光博客
Latest news
Latest news
小众软件
小众软件
Attack and Defense Labs
Attack and Defense Labs
I
Intezer
Y
Y Combinator Blog
The Last Watchdog
The Last Watchdog
大猫的无限游戏
大猫的无限游戏

博客园 - PKICA

rust自定义线程属性std::thread::Builder rust关联函数 rust高并发设计实践进阶 rust高并发设计实践 rust性能优化与安全边界 联合体union在rust和C语言中有什么区别 rust并发与异步编程 如何预防rust不良的代码设计总结 rust类型系统与零成本抽象 rust内存模型 告别大显存依赖!用 Rust 新一代深度学习框架 Burn 打造纯 CPU 文本分类推理引擎 rust类型系统标记 编译配置解答 git实用命令 rust底层设计理念值得注意的几个地方总结 rust可变引用作为函数参数的机理详解 Rust内存重解释transmute C与Rust类型映射 Rust FFI 安全抽象范式 rust延迟初始化原语 rust重借用机制与原理 rust参数传递模型 汇编语言语法详解 gdb汇编调试 gdb-pwndbg的安装与使用指南 gdb调试插件gef C语言thread_local linux系统readelf命令使用指南 gcore转储进程内存 gdb查看命令 RGB与YUV颜色编码的区别 Rust原子类型 C++ STL求两个集合交集差集 gdb调试集锦 ubuntu24.0.4使用root用户登录 ubuntu24.0.4输入密码后跳回登录界面 AI内存压缩技术TurboQuant及存疑 ubuntu切换到指定内核版本 在没有顶级科技大佬直接背书的情况下deepseek为啥能够异军突起? HuggingFace和deepseek的关系 当前主流AI大模型 Rust写时克隆Cow系列2
rust线程
PKICA · 2026-07-23 · via 博客园 - PKICA

Rust 的线程模型基于底层操作系统的 1:1 线程模型,这意味着一个系统线程直接对应一个 Rust 线程。Rust 通过其独创的所有权(Ownership)和借用(Borrowing)机制,在编译期就能杜绝并发编程中最头疼的“数据竞争”问题。

1. 基础:创建线程与等待

在 Rust 中,使用标准库的 std::thread::spawn 可以轻松创建一个新线程。

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

fn main() {
    // 创建一个新线程
    let handle = thread::spawn(|| {
        for i in 1..5 {
            println!("新线程: {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

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

    // join() 会阻塞当前线程,直到 handle 对应的线程执行完毕
    handle.join().unwrap(); 
}
  • 主线程结束,程序即结束:如果主线程先执行完,新线程会被直接强行终止。
  • join() 方法:通过调用 handle.join(),主线程会等待子线程运行结束,确保任务完整执行。

1). 为什么 handle.join() 后面必须加 .unwrap()

在 Rust 中,handle.join() 返回的不是普通的 Result<T, E>,而是一个特殊的类型:
Result<T, Box<dyn Any + Send + 'static>> [1]

当子线程运行结束时,主线程通过 join() 会收到两种完全不同的执行结果。.unwrap() 的本质,是为了在子线程发生崩溃(Panic)时,让主线程也跟着一起崩溃,防止错误被无意中掩盖。

我们可以把这两类结果拆解来看:

    • Ok(T):子线程正常运行结束。.unwrap() 会直接解包并返回子线程闭包中的返回值 T [1]。
    • Err(Box<dyn Any + Send>):子线程在运行过程中发生了崩溃(Panic) [1]。Rust 的线程设计具有“Panic 隔离”特性:一个子线程挂了,默认不会导致整个进程挂掉。如果你不用 .unwrap() 去解包,主线程就会拿到这个 Err 并继续若无其事地往下走,这在大多数并发业务中是非常危险的。通过 .unwrap(),一旦子线程崩溃,主线程在解包时也会立即触发 Panic(即崩溃传递),确保系统能够及时暴露问题。

线程状态

1). Joinable(可连接状态)

在 Rust 中,通过 std::thread::spawn 创建的线程默认都是 Joinable 的。

特点

  • 生命周期受控:主线程可以(也通常应该)显式等待该线程结束。
  • 资源回收机制:当子线程执行完毕后,它的退出状态和部分底层资源会保留在系统中,直到有其他线程对其调用 .join(),这些资源才会被彻底清空。
  • 返回值传递:Joinable 线程允许将子线程中最后一行表达式的结果(或错误)返回给调用 .join() 的线程。

Rust 中的表现:JoinHandle

thread::spawn 会返回一个名为 JoinHandle<T> 的结构体。它就是操作该线程的“把手”。

2). Detached(分离状态)

分离状态意味着线程与创建它的父线程“断开”了联系。一旦线程被分离,它将完全脱离宿主线程的控制,由操作系统独立管理。

特点

  • 无法被等待:你不能对一个分离的线程调用 .join(),也无法得知它何时结束。
  • 无法获取返回值:子线程的计算结果或崩溃信息(Panic)无法直接传递回主线程。
  • 自动回收资源:当分离的线程执行完毕后,操作系统会自动立即回收它所占用的所有资源,不会留下任何痕迹。
  • 随主线程消亡:虽然它被称为“独立”,但如果主线程(main 进程)退出了,所有分离的子线程依然会被操作系统强制杀死。

Rust 中如何创建分离线程?

在 Rust 中,你不需要显式调用类似 detach() 的函数。Rust 巧妙地利用了所有权系统(Drop 机制)来实现分离:

规矩:如果你让 JoinHandle 离开了作用域(被销毁/Drop),且没有调用 .join(),那么这个线程在底层就会被隐式地转为分离(Detached)状态。

2. 线程安全的关键:move 闭包

当你想在子线程中使用主线程的变量时,普通的闭包会因为“借用检查”而报错(因为 Rust 无法确定主线程的变量能活得比子线程久)。此时必须使用 move 关键字。

use std::thread;

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

    // 使用 move 关键字将 v 的所有权强制转移(move)到新线程中
    let handle = thread::spawn(move || {
        println!("子线程使用 vector: {:?}", v);
    });

    // 此处不能再使用 v,因为所有权已经没了
    // println!("{:?}", v); // 编译报错!

    handle.join().unwrap();
}

3. 线程间通信:通道(Channel)

Rust 标准库提供了 mpsc(Multi-producer, Single-consumer,多生产者,单消费者)通道,专门用于线程间传递数据。

use std::sync::mpsc;
use std::thread;

fn main() {
    // 创建通道,tx 是发送端(Sender),rx 是接收端(Receiver)
    let (tx, rx) = mpsc::channel();

    // 克隆一个发送端,用于多个生产者
    let tx1 = tx.clone();

    // 生产者线程 1
    thread::spawn(move || {
        tx1.send(String::from("来自线程 1 的问候")).unwrap();
    });

    // 生产者线程 2
    thread::spawn(move || {
        tx.send(String::from("来自线程 2 的问候")).unwrap();
    });

    // 消费者(主线程)接收数据
    for received in rx {
        println!("收到: {}", received);
    }
}
  • send 方法会转移数据的所有权,防止发送后数据被修改引发混乱。
  • 当所有发送端(tx)都被释放时,迭代器 rx 会自动结束循环。

4. 共享内存并发:ArcMutex

如果你需要多个线程同时读写同一块内存数据,通道就不适用了。Rust 使用原子引用计数(Arc)和互斥锁(Mutex)来实现安全的共享内存。

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

fn main() {
    // Mutex 提供互斥访问,Arc 提供跨线程的多重所有权
    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 || {
            // lock() 获取锁,如果锁被占用则等待。
            // lock_num 是一个智能指针(MutexGuard),离开作用域时自动解锁!
            let mut num = counter.lock().unwrap();
            *num += 1; 
        });
        handles.push(handle);
    }

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

    println!("最终计数结果: {}", *counter.lock().unwrap());
}

5. 线程安全底层:SendSync 特征

Rust 的并发安全并不是魔法,而是基于类型系统的两个内置特质(Trait):

  • Send:表明该类型的数据可以转移到其他线程(绝大多数类型都是 Send,但 Rc<T> 不是)。
  • Sync:表明该类型的数据可以被多个线程同时通过引用传递(如果 TSync,那么 &T 就是 Send)。

只要你在编写并发代码时违反了这两个特质,编译器就会直接拒绝编译,从而在源头上杜绝了运行时的并发 Bug。


💡 进阶建议

  • 不要过度使用原生线程:操作系统线程的创建和上下文切换是有开销的。
  • 计算密集型任务:推荐使用生态库 rayon 自动实现线程池和并行计算。
  • I/O 密集型任务(如网络请求):推荐使用 async/await 异步编程(如 tokio 运行时),而不是为每个请求开一个原生线程。

参考资料:

《Rust 权威指南》