示例工程教程【免费下载链接】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点击查看免费下载Sync是 Rust 标准库中与Send齐名的两大并发标记 trait 之一它定义了“一个类型能否被多个线程安全地共享”这一基本问题。本文以 book/src/07_threads/14_sync.md 为核心骨架结合仓库中线程章节的MutexGuard、RefCell与ArcMutexT实战代码完整讲解Sync的定义、与Send的四种组合关系、编译器如何强制保证以及这些规则如何支撑多线程 ticket store 项目的锁设计。读完你将能准确判断任意类型能否跨线程共享并理解为何ArcMutexT是 Rust 并发编程中最常见的共享状态载体。收官之课线程章节为何以Sync收尾在 book/src/07_threads/00_intro.md 的开篇作者就预告了本章将覆盖 Rust 核心并发特性的完整清单线程std::thread、消息传递channel、共享状态Arc/Mutex/RwLock以及“编码 Rust 并发保证的两个 traitSend和Sync”。整个章节的路线图是Send在 book/src/07_threads/11_locks.md 中详细展开——它决定了“值能否安全地从一个线程转移到另一个线程”并引出了MutexGuard不可发送的编译错误和Arc的解决方案Sync则由 book/src/07_threads/14_sync.md 作为章节的压轴内容补齐另一半与Send一起构成 Rust 并发安全保证的完整拼图。配套练习 exercises/07_threads/14_sync/src/lib.rs 的注释写得很直白“Not much to be exercised onSync, just a thing to remember”Sync没什么可动手练习的只需记住这件事整份练习只有一个outro函数需要填空fn outro() - static str { I have a good understanding of __! }其测试 exercises/07_threads/14_sync/src/lib.rs 要求补全为assert_eq!(outro(), I have a good understanding of Send and Sync!);可见作者刻意将Sync定位为“理解性”而非“操作性”的知识点它不要求你写复杂代码而是要求你真正建立对并发类型系统的全局认知。Sync是什么与Send互为镜像的自动 traitSync的定义非常简洁原文给出了两句话Sync是自动 traitauto trait和Send一样所有能在线程之间**安全地共享shared**的类型编译器都会自动为它实现Sync。用一句话概括就是T是Sync的当且仅当T是Send的。这一定义精确地刻画了Send与Sync的分工Send关心的是“移动”T: Send意味着整个T值可以安全地从一个线程转移到另一个线程所有权随之转移原线程不再持有它Sync关心的是“共享”T: Sync意味着多个线程可以同时持有T这样的共享引用各自只读地访问同一个底层数据。“T: Sync等价于T: Send”这个表述很精妙把“共享T”这件事重新表述为“把一个T引用发送给另一个线程”。引用本身只是一个指针大小的值发送它很容易难点在于发送之后原线程仍保留着这个引用指向的数据此时两个线程就同时持有指向同一数据的指针——这正是Sync要保证安全的场景。作为自动 traitSync与Send一样由编译器根据类型的定义自动推导或自动不推导无需手动实现。开发者也可以为自定义类型手动实现Sync但和Send一样这必须通过unsafe impl完成因为你需要向编译器做出它无法自动验证的保证。由于绝大多数类型都能安全共享编译器采取“默认实现、发现危险字段才豁免”的保守策略。T: Sync不蕴含T: Send以MutexGuard为例文档强调的第一个反直觉事实是T可以是Sync的同时不是Send的。标准库中的MutexGuard就是最典型的例子它不是Send但是Sync。为什么会这样文档给出了两点理由不是Send的原因锁必须在获取它的那个线程上释放。MutexGuard的析构函数负责释放锁参见 book/src/07_threads/11_locks.md 中关于Mutex使用内部可变性、lock只需self的讨论如果允许把MutexGuard发送到另一个线程并在那里被 drop锁就会在错误的线程上被释放而这在部分平台上依赖于操作系统底层原语的约束属于未定义行为。因此我们不希望MutexGuard在别的线程被 drop。是Sync的原因把MutexGuard共享给另一个线程对这个锁在哪里释放没有任何影响。共享引用不能移动或拥有MutexGuard本身它只是让另一个线程“看到”这个 guard 的存在锁仍然由持有 guard 的原线程负责释放。共享引用既不转移所有权也不触发析构因此完全安全。这个例子的价值在于它精确区分了两个维度Send禁止的是所有权/析构位置的跨线程迁移Sync允许的只是共享只读视角的跨线程传播。T: Send不蕴含T: Sync以RefCell为例反向的推论同样成立文档给出第二个经典案例T可以是Send的同时不是Sync的。标准库中的RefCellT就是典型代表在T: Send的前提下RefCellT: Send但RefCellT不实现Sync。RefCellT在运行时进行借用检查它内部维护着记录当前借用状态的计数器。问题在于这些计数器不是线程安全的。如果多个线程同时持有RefCellT它们各自都可以尝试调用borrow_mut()获取可变引用——由于计数器操作没有原子性保证多个线程可能同时读到“当前无人借用”的状态从而同时获得多个指向同一数据的可变引用造成数据竞争data race。而Send对这个类型是安全的理由恰好与上面互补当我们把一个RefCellT发送到另一个线程时原线程不再持有任何指向其内部数据的引用。所有权整体迁移没有并发访问的可能自然也就没有数据竞争的风险。RefCellT与MutexT的对照在此非常鲜明RefCellT提供单线程内的运行时借用检查内部可变性代价是非Sync不能在多线程间共享MutexT提供跨线程的运行时互斥访问其内部计数器是原子操作因此在T: Send时是Sync的。这解释了为什么多线程场景下必须用Mutex/RwLock取代RefCellRefCell解决的是“借用规则在运行时才满足”的问题Mutex额外解决了“这个运行时检查必须跨线程原子进行”的问题。四象限全景Send与Sync的组合关系综合文档中的两个例子Send与Sync可以构成四种组合理解这张表是掌握 Rust 并发类型系统的关键组合含义典型类型SendSync既能跨线程转移所有权也能跨线程共享引用i32、String、VecTT: Send、ArcT、MutexTT: SendSend非Sync可以整体移动但不能共享引用RefCellT、CellTT: Send非SendSync不能移动所有权但可以共享引用MutexGuard_, T、RwLockReadGuard/RwLockWriteGuard非Send非Sync既不能移动也不能共享RcT引用计数非原子标准库行为、裸指针文档中MutexGuard属于第三象限Sync而非SendRefCellT属于第二象限Send而非Sync而仓库线程章节中反复使用的ArcMutexTicket则落在第一象限。绝大多数基本类型数字、布尔、引用计数为原子的智能指针、锁本身都处于第一象限这也是它们能直接参与并发编程的原因。项目实战验证ArcMutexT与锁设计中的Sync保证Sync并非孤立的理论概念仓库中多线程 ticket store 的实现处处依赖这些保证。以细粒度锁版本的 exercises/07_threads/12_rw_lock/src/store.rs 为例核心状态被设计为#[derive(Clone)] pub struct TicketStore { tickets: BTreeMapTicketId, ArcMutexTicket, counter: u64, }这个设计能编译、能在多线程间安全共享依赖的正是Send/Sync的层层组合Ticket本身Send它由TicketId、TicketTitle、TicketDescription、Status等纯数据字段组成见 exercises/07_threads/12_rw_lock/src/data.rs没有Rc、裸指针等危险成员编译器自动推导其SendMutexTicket是Sync的标准库为MutexT在T: Send时实现Sync——这正是它作为“线程安全共享容器”的资格来源ArcMutexTicket是SendSync的Arc使用原子引用计数多个线程可以克隆Arc并把克隆发送Send或共享Sync出去get方法通过.cloned()返回Arc句柄见 exercises/07_threads/12_rw_lock/src/store.rs客户端拿到句柄后即可在任意线程安全地加锁读写。与之形成对照的是如果改用RcRefCellTicket由于Rc非Send非Sync、RefCell非Sync整个设计将无法跨线程共享——这正是 book/src/07_threads/11_locks.md 中引入Arc的根本动机“owned shared reference”即“拥有的共享引用”。Sync还渗透在thread::spawn的签名约束中。如 book/src/07_threads/02_static.md 所示pub fn spawnF, T(f: F) - JoinHandleT where F: FnOnce() - T Send static, T: Send static闭包只需Send闭包被整体移动进新线程无需Sync。而一旦你想让多个线程共享某个T例如在 book/src/07_threads/13_without_channels.md 中讨论的“移除 channel、让客户端直接访问共享的TicketStore”方案Sync就成为绕不开的硬约束要么用Arc让每个线程持有一份句柄要么用Mutex/RwLock把访问串行化二者共同作用的前提都是相关类型满足Sync。编译器如何守护Sync自动推导与unsafe impl作为自动 traitSync的强制力来自编译器而不是开发者的自觉自动推导编译器逐字段检查类型的组成。一个类型是Sync的当且仅当其所有字段都是Sync的一旦混入RefCell、Rc、裸指针等非Sync成员整个类型立即失去Sync资格。同理Send也是逐字段推导的。组合传播ArcT、MutexT、RwLockT等组合器会把底层类型的性质“升级”为跨线程能力但前提依然是底层T本身满足相应约束。unsafe impl Sync极少数情况下如自研的原语封装开发者可以手动声明impl Sync for T但必须由自己保证“多个线程同时持T绝对安全”编译器不再提供任何检查——这与 book/src/07_threads/11_locks.md 中对Send的说明完全一致。这套机制的意义在于数据竞争在 Rust 中不是运行时问题而是编译期问题。只要代码中出现了跨线程共享非Sync类型的引用编译器就会在编译阶段拒绝而不是等程序在运行时崩溃。这正是本仓库开篇所承诺的 fearless concurrency——并发代码的“胆量”来自类型系统的事先背书。练习收官与自查清单最后一个练习 exercises/07_threads/14_sync 是独立的 Cargo crate进入该目录运行cargo test即可验证outro是否补全正确cd exercises/07_threads/14_sync cargo test在提交答案前可以用下面这张清单自查是否真正理解了Sync能否用自己的话说出“T: Sync当且仅当T: Send”的含义能否解释为什么MutexGuard是Sync但不是Send锁必须由获取它的线程释放能否解释为什么RefCellT是Send但不是Sync借用计数器非线程安全共享会导致数据竞争能否立即说出RcRefCellT、ArcMutexT、MutexGuard、RefCell各自落在四象限的哪一格能否说明thread::spawn的闭包约束是Send static而跨线程共享需要的是Sync总结Sync是 Rust 并发类型系统的另一半支柱Send管“移动”Sync管“共享”二者通过“T: Sync即T: Send”这一精妙定义统一起来。MutexGuard与RefCell这两个看似矛盾的例子恰恰揭示了类型系统如何在不同维度上独立建模“安全”锁的释放位置归Send管借用检查的原子性归Sync管。回到本项目从 book/src/07_threads/11_locks.md 的Send到 book/src/07_threads/14_sync.md 的Sync整个线程章节最终教会我们真正可用的多线程共享状态一定是ArcMutexT这类同时满足Send与Sync的组合而这一切都由编译器在编译期替你守住了底线。赞分享示例工程教程【免费下载链接】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深入理解 Rust 生命周期Lifetimes与 IntoIterator for TicketStore100 exercises to learn rust深入理解 Rust 生命周期Lifetimes与 IntoIterator for TicketS示例工程教程100-exercises-to-learn-rust 第 13 讲为 TicketStore 实现 Index 索引 trait100 exercises to learn rust 第 13 讲为 TicketStore 实现 Index 索引 trait 导读 在《100 exer示例工程教程date-fns 快速入门在浏览器与 Node.js 中使用现代 JavaScript 日期工具库date fns 快速入门在浏览器与 Node.js 中使用现代 JavaScript 日期工具库 导读 本文基于 date fns 官方 Getting示例工程教程上一篇Fast-GitHub终极指南如何让GitHub下载速度提升100倍的免费浏览器插件下一篇GitHub加速实战指南突破国内访问瓶颈的高效方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考