
示例工程教程【免费下载链接】100-exercises-to-learn-rustA self-paced course to learn Rust, one exercise at a time.项目地址https://gitcode.com/GitHub_Trending/10/100-exercises-to-learn-rust点击查看免费下载本篇技术指南围绕 100-exercises-to-learn-rust 课程第 7 章Threads的第一课展开讲解操作系统中线程的本质、Rust 程序启动时的main线程以及标准库std::thread模块中spawn、join等核心 API 的用法。读完本篇你将理解线程与进程、逻辑执行与物理执行的关系掌握在 Rust 中创建线程、等待线程结束以及理解主线程退出即进程退出这一关键语义并为本课程后续的static生命周期与作用域线程章节打下基础。背景从单线程到多线程的转折点在进入本篇之前课程的 Threads 章节引言 已经交代了清晰的脉络Rust 的核心承诺之一是fearless concurrency无畏并发让编写安全并发程序变得更容易。但在此之前的全部练习都是单线程的——从基本计算器、票据模型 v1/v2 到票据管理系统代码都在同一个线程中顺序执行。本章开始课程将把前面构建的票据存储ticket store改造成多线程版本并借此覆盖 Rust 大部分核心并发特性std::thread线程、channel 消息传递、Arc/Mutex/RwLock共享状态以及编码 Rust 并发保证的Send与Synctrait。本篇要讨论的正是这一切的起点线程本身是什么以及 Rust 中最基础的线程 API。什么是线程操作系统管理下的执行上下文线程thread是由底层操作系统管理的执行上下文execution context。每个线程拥有自己独立的栈stack和指令指针instruction pointer。栈用于存放局部变量和调用帧指令指针则记录当前正在执行的指令位置——这两者共同决定了这个线程现在执行到哪一步、它的局部状态是什么。线程与进程的关系需要注意区分一个**进程process**可以管理多个线程这些线程共享同一个内存空间因此它们能够访问同一份数据。共享内存正是多线程编程的威力所在也是数据竞争等并发问题的根源。在 Rust 中共享数据但保证安全正是所有权系统与后文将要涉及的Send/Synctrait 要解决的课题。逻辑执行 vs 物理执行调度器的作用线程是一种**逻辑logical构造。物理上一个 CPU 核心在同一时刻只能运行一组指令CPU 核心才是物理physical执行单元。由于线程的数量往往远多于 CPU 核心数操作系统内核中的调度器scheduler**负责决定在任意时刻运行哪个线程将 CPU 时间切分给各个线程以最大化吞吐量和响应性。这解释了多线程程序的一个重要直觉多个同时运行的线程实际上是在共享的 CPU 资源上被调度器快速切换执行的。当你写出两个看起来并行的循环时底层可能是调度器在它们之间轮转分配时间片。程序起点main线程当一个 Rust 程序启动时它运行在单个线程上这个线程被称为主线程main thread。主线程由操作系统创建负责执行main函数。课程文档给出了一个最简单的验证示例use std::thread; use std::time::Duration; fn main() { loop { thread::sleep(Duration::from_secs(2)); println!(Hello from the main thread!); } }这个程序每秒此处为每 2 秒打印一次消息永不退出。它演示了单线程程序的执行方式代码在main线程上顺序执行thread::sleep让出 CPU 一段时间后再继续。std::threadRust 标准库的线程模块Rust 标准库提供了std::thread模块用于创建和管理线程。它是后续一切并发练习的基础设施。在本课程的 多线程票据存储改造 中std::thread::spawn将用于启动独立的服务器线程来消费 channel 中的命令。spawn创建新线程使用std::thread::spawn可以创建新线程并在其上执行代码。它接收一个闭包闭包内的代码会在新的操作系统线程上运行并返回一个JoinHandleT类型的句柄。课程文档的核心示例use std::thread; use std::time::Duration; fn main() { let handle thread::spawn(|| { loop { thread::sleep(Duration::from_secs(1)); println!(Hello from a thread!); } }); loop { thread::sleep(Duration::from_secs(2)); println!(Hello from the main thread!); } }运行这个程序你会看到主线程与新派生的线程并发执行主线程每 2 秒打印一次 Hello from the main thread!而派生线程每 1 秒打印一次 Hello from a thread!。两个线程各自独立推进互不阻塞对方。这正是每个线程独立取得进展的直接体现。进程终止语义主线程退出即进程退出理解spawn之后紧接着需要掌握一个关键语义——进程终止规则当主线程结束时整个进程将退出。派生的线程会一直运行直到它自身完成或者主线程结束。文档中的第二个示例揭示了这一点use std::thread; use std::time::Duration; fn main() { let handle thread::spawn(|| { loop { thread::sleep(Duration::from_secs(1)); println!(Hello from a thread!); } }); thread::sleep(Duration::from_secs(5)); }在这个例子中派生线程理论上会永远循环打印但主线程在sleep5 秒后返回、main函数结束整个进程随之退出。因此你预计只会看到 Hello from a thread! 打印大约五次——主线程结束后即使派生线程的循环没有终止条件它也被进程退出强行终止了。这一语义对后续章节至关重要正因为thread::spawn派生的线程可能比它的父线程活得更久甚至运行到进程结束spawn才要求闭包满足static生命周期约束——详见课程的static生命周期章节。join等待线程结束spawn返回的JoinHandle提供了join方法用于等待派生线程完成use std::thread; fn main() { let handle thread::spawn(|| { println!(Hello from a thread!); }); handle.join().unwrap(); }在这个例子中主线程会等待派生线程结束后才退出。这引入了两个线程之间的**同步synchronization**机制由于主线程直到派生线程完成才会退出你可以保证在程序退出之前一定看到 Hello from a thread! 这条消息打印出来。join的返回值是ResultT, Boxdyn Any Send如果派生线程正常结束返回Ok(T)如果线程发生了 panic返回Err(...)。因此示例中使用unwrap()解包。实战练习用spawn与join实现多线程求和本篇对应的练习题位于 exercises/07_threads/01_threads/src/lib.rs任务是实现一个多线程版本的sum函数use std::thread; pub fn sum(v: Veci32) - i32 { todo!() }练习要求给定一个整数向量将其分成两半每一半在一个独立线程中求和。题目同时给出了一条关键提示你将无法让派生线程直接借用向量的切片。你需要为原始向量的每一半分配新的向量。我们将在下一个练习中看到为什么这是必要的。这正是上一篇文档所述static约束的直观体现thread::spawn的闭包要求static生命周期直接借用局部向量切片无法满足该约束因此必须通过move将自有数据新建的左右两个向量移入闭包。与之形成对比的是课程的 作用域线程章节 中std::thread::scope的做法——作用域线程可以安全借用环境中的变量因为所有线程在作用域结束前会被自动join。练习的测试用例位于同一文件的tests模块验证了正确性而非实现方式覆盖空向量和为 0、单个元素、5/9/10 个元素等场景例如sum(vec![1, 2, 3, 4, 5]) 15。题目注释也提醒测试只能验证结果正确你完全可以只写v.iter().sum()通过测试但那会失去练习的意义——本练习的目的是亲手体验spawn与join的配合。本篇要点小结线程是操作系统管理的执行上下文每个线程有自己的栈和指令指针一个进程可含多个线程它们共享内存空间。线程是逻辑构造CPU 核心是物理执行单元操作系统调度器负责在众多线程间切分 CPU 时间。Rust 程序从main线程开始执行。std::thread::spawn创建新线程并返回JoinHandle闭包中的代码在新线程上运行。主线程结束即进程结束派生线程即使未完成也会被强制终止。JoinHandle::join让主线程等待派生线程完成是基本的线程间同步手段。spawn的闭包要求static生命周期无法直接借用非静态数据——这是下一个练习static生命周期要深入讨论的主题。至此你已经掌握了 Rust 并发编程的地基。下一课将解释为什么上一篇练习中借用切片会报出static错误以及泄漏内存这种看似危险的操作在某些场景下反而是合理策略见 Leaking data。在课程后续章节中spawn与join将用于启动票据存储的服务器线程并通过 channel 与其他线程通信逐步拼出完整的多线程系统。赞分享示例工程教程【免费下载链接】100-exercises-to-learn-rustA self-paced course to learn Rust, one exercise at a time.项目地址https://gitcode.com/GitHub_Trending/10/100-exercises-to-learn-rust点击查看免费下载相关推荐深入 Rust 并发基石static 生命周期与 thread::spawn 的借用约束100-exercises-to-learn-rust深入 Rust 并发基石 static 生命周期与 thread::spawn 的借用约束100 exercises to learn rust st示例工程教程100-exercises-to-learn-rust 第 7 章用 Rust 无畏并发重构多线程 Ticket Store100 exercises to learn rust 第 7 章用 Rust 无畏并发重构多线程 Ticket Store 导读 本章是《100 exerc示例工程教程10分钟上手Paparazzi UAV从安装到首飞的完整入门指南10分钟上手Paparazzi UAV从安装到首飞的完整入门指南 Paparazzi UAV是一款免费开源的无人机系统软件支持固定翼、旋翼、混合式等多种飞行嵌入式物联网上一篇NetBox 设备Device模型完全指南从字段语义到源码级校验逻辑下一篇CANN ops-math 算子解析Acos 反余弦算子的 aclnn 接口原理与调用实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考