当前位置: 首页 > news >正文

【Rust】17-Send、Sync 与并发安全抽象

Send、Sync 与并发安全抽象

研究目标

  • 理解SendSync如何表达跨线程安全。
  • 区分所有权转移、共享引用和内部可变性。
  • 掌握 Rust 并发抽象背后的类型约束。

Rust 并发安全的基础

Rust 的并发安全不是来自某个单独的锁库,而是所有权、借用和类型系统共同作用的结果。核心原则是:

  • 数据可以被移动到另一个线程,但必须安全。
  • 数据可以被多个线程共享引用,但共享访问必须安全。
  • 可变共享需要同步原语或原子操作。

SendSync是两个标记 trait,用于表达这些性质。

Send

Send表示一个类型的值可以安全地转移到另一个线程。

usestd::thread;fnmain(){lettext=String::from("hello");lethandle=thread::spawn(move||{println!("{text}");});handle.join().unwrap();}

StringSend,所以可以通过move闭包转移到新线程。

反例是Rc<T>

usestd::rc::Rc;usestd::thread;fnmain(){letvalue=Rc::new(1);// thread::spawn(move || {// println!("{value}");// });}

Rc<T>的引用计数更新不是线程安全的,所以它不是Send

Sync

Sync表示一个类型可以安全地被多个线程通过共享引用访问。形式化地说,如果&TSend,那么TSync

i32StringVec<T>在合适条件下都是Sync,因为多个线程共享不可变引用读取它们是安全的。

RefCell<T>不是Sync

usestd::cell::RefCell;fnmain(){letvalue=RefCell::new(1);*value.borrow_mut()+=1;}

RefCell<T>的借用计数是非线程安全的运行时检查,不能被多个线程同时访问。

Arc 与 Mutex

跨线程共享所有权通常使用Arc<T>

usestd::sync::Arc;usestd::thread;fnmain(){letvalue=Arc::new(String::from("shared"));letcloned=Arc::clone(&value);lethandle=thread::spawn(move||{println!("{cloned}");});println!("{value}");handle.join().unwrap();}

如果需要跨线程修改共享数据,使用Mutex<T>

usestd::sync::{Arc,Mutex};usestd::thread;fnmain(){letcounter=Arc::new(Mutex::new(0));letmuthandles=Vec::new();for_in0..4{letcounter=Arc::clone(&counter);handles.push(thread::spawn(move||{letmutguard=counter.lock().unwrap();*guard+=1;}));}forhandleinhandles{handle.join().unwrap();}println!("{}",*counter.lock().unwrap());}

Arc<Mutex<T>>的含义是:多个线程共享同一个所有权句柄,每次修改前先获得互斥锁。

RwLock 与读多写少

RwLock<T>允许多个读者或一个写者:

usestd::sync::RwLock;fnmain(){letvalue=RwLock::new(vec![1,2,3]);{letread=value.read().unwrap();println!("{}",read.len());}{letmutwrite=value.write().unwrap();write.push(4);}}

读多写少场景中,RwLock可能比Mutex更合适。但具体性能取决于锁实现、竞争程度和临界区大小。

原子类型

简单计数器可以使用原子类型:

usestd::sync::atomic::{AtomicUsize,Ordering};usestd::sync::Arc;usestd::thread;fnmain(){letcounter=Arc::new(AtomicUsize::new(0));letmuthandles=Vec::new();for_in0..4{letcounter=Arc::clone(&counter);handles.push(thread::spawn(move||{counter.fetch_add(1,Ordering::Relaxed);}));}forhandleinhandles{handle.join().unwrap();}println!("{}",counter.load(Ordering::Relaxed));}

原子操作避免锁,但内存序选择很重要。Relaxed只保证原子性,不建立跨线程同步顺序。复杂并发算法应谨慎使用原子并配合测试和模型检查。

Channel

另一种并发思路是消息传递:

usestd::sync::mpsc;usestd::thread;fnmain(){let(tx,rx)=mpsc::channel();thread::spawn(move||{tx.send(String::from("hello")).unwrap();});letmessage=rx.recv().unwrap();println!("{message}");}

channel 通过移动消息所有权减少共享状态。很多系统可以优先用消息传递表达工作流,只在必要时共享可变状态。

自动实现与 unsafe impl

SendSync通常由编译器自动推导。如果类型的所有字段都是Send,该类型一般也是SendSync也类似。

手写unsafe impl Sendunsafe impl Sync意味着你向编译器承诺这个类型满足跨线程安全不变量:

structMyPointer(*mutu8);// unsafe impl Send for MyPointer {}

这非常危险。除非你完全理解内部别名、生命周期、同步和释放规则,否则不要手写这些实现。

async 中的 Send

异步任务也常遇到Send

tokio::spawn(asyncmove{// future 必须 Send + 'static});

如果 future 跨.await保存了非 Send 值,整个 future 就不是 Send。解决方式包括改用Arc、缩小非 Send 值作用域、使用本地任务执行器。

常见误解

  • Arc<T>只提供线程安全引用计数,不让T自动可变。
  • Mutex<T>保护的是数据访问,不是让逻辑自动无死锁。
  • Send是所有权跨线程转移,Sync是共享引用跨线程访问。
  • RefCell<T>是单线程内部可变性,不是线程同步工具。

继续研究

  • Rustonomicon:Send and Sync。
  • Rust Book:fearless concurrency。
  • Rust Reference:marker traits、undefined behavior、data races。
  • Loom:并发模型测试工具。

后记

2026年6月11日15点23分于上海。

http://www.jsqmd.com/news/997625/

相关文章:

  • 2026拼多多代运营公司推荐:百亿补贴+拼便宜组合拳,销量利润双增长 - 百推信源
  • MATLAB刀具路径B样条拟合与拐点平滑衔接工具包
  • 2026 年 6 月最新|靠谱台车式退火炉源头厂家推荐,非标定制节能热处理炉优选 - 商业新知
  • 2026年通辽装修公司深度对比:全屋定制硬核差距惊人拆解 - 国麟测评
  • 2026年重型货架厂家怎么选?从台州、成都到中山,这些正规厂商值得关注! - 优质品牌商家
  • ChatGLM2-6B模型拆解:Prefix Decoder架构如何融合双向与单向注意力?
  • 2024广州民办高中测评:零基础择校避坑指南 - 服务品牌热点
  • 2026台州卫生间漏水不用砸砖?微创补漏靠谱方案 - 苏易修缮
  • 2026年好用的视频去水印软件有哪些?视频去水印软件推荐实用教程
  • F28335的I2C时钟配置踩坑实录:从400kHz降到100kHz才稳定的背后
  • AI写论文绝佳选择,4款AI论文写作工具,轻松打造高质量论文!
  • 保姆级教程:用Nav2行为树给你的机器人导航加上“智能大脑”(附完整XML配置)
  • 【Rust】18-宏系统:声明宏、过程宏与代码生成
  • 2026年长春小提琴培训行业观察:教学体系、师资结构与学员成长路径分析 - 优质品牌商家
  • 2026深圳黄金回收便民服务指南,规范门店名录与特色优势全览! - 奢侈品交易观察员
  • 2026 湖州厨卫屋面地下室漏水瓷砖空鼓测评:吉修匠 99.8 分五星榜首 - 吉修匠
  • Windows下可直接运行的模板旋转匹配工具:自动输出XY坐标和旋转角度
  • Windows右键菜单终极清理指南:一键告别臃肿菜单的完整教程
  • Hugging Face Transformers:从模型加载到边缘部署的工业级AI工作流
  • 从《宫娥》到《睡莲》:技术博主如何用图像学方法看懂艺术史里的“密码”?
  • 从汽车级EEPROM选型到开源磨损均衡算法:手把手教你设计高可靠嵌入式存储模块(附避坑指南)
  • 伪Anosov流与双曲3-流形构造技术解析
  • 深入MAX30102算法核心:手把手解读心率血氧计算函数,告别‘黑盒’调用
  • 别再死记硬背了!用Python 3.10手把手模拟TDM时分复用,5分钟搞懂同步与异步
  • 从Betaflight到Ardupilot:为什么你的AT32飞控板还跑不了?聊聊ChibiOS移植的那些坑
  • 拼多多代运营公司怎么样?拼多多代运营公司手福音,保姆式托管 + 全流程代操作(附联系方式) - 百推信源
  • 从EMV到物联网:TLV编码的前世今生与实战避坑指南
  • Python 高手编程系列三千四百四十三:setup.cfg
  • 从玩具车到真汽车:聊聊EEPROM磨损均衡算法在Arduino和STM32上的开源实现
  • 如何用ImageSearch在5分钟内实现本地图像搜索:千万级图片库管理终极指南