Rust 学习笔记(36)-Send与Sync

参考章节《Rust语言圣经(Rust Course)》第3.6.6章 基于 Send 和 Sync 的线程安全

我们先来看看如下代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
use std::thread;
use std::rc::Rc;
fn main() {
    let v = Rc::new(5);
    let t = thread::spawn(move || {
        println!("{}",v);
    });

    t.join().unwrap();
}

这段代码会报错,其中关键的一句是

1
2
3
error[E0277]: `Rc<i32>` cannot be sent between threads safely
--省略部分报错--
        = help: within `[closure@src/main.rs:5:27: 7:6]`, the trait `Send` is not implemented for `Rc<i32>`

它告诉我们 Rc<i32> 不能在线程间安全的传递,因为其未实现 Send 特征

什么是Send 和 Sync 特征

SendSync 是 Rust 安全并发的重中之重,但是实际上它们只是标记特征(marker trait,该特征未定义任何行为,只是用于标记)

我们来看看它有什么用

  1. 实现 Send 的类型可以在线程间安全的传递其所有权
  2. 实现 Sync 的类型可以在线程间安全的共享(通过引用)

这里还有一个潜在的依赖关系:一个类型想要在线程间安全的共享,那么它引用的值必须能在线程间传递
这也就意味着若类型 T 实现了 Sync,则类型 T 引用的值必须实现 Send

Rc 和 Arc 源码对比

好了,了解了SyncSend 的作用后,我们再来看看RcArc 的源码

1
2
3
4
5
6
7
// Rc源码片段
impl<T: ?Sized> !marker::Send for Rc<T> {}
impl<T: ?Sized> !marker::Sync for Rc<T> {}

// Arc源码片段
unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}

!代表移除特征的相应实现,上面代码中 Rc<T>SendSync 特征被特地移除了实现,而
Arc<T>则相反,其实现了 Sync + Send,再结合之前的编译器报错,大概可以明白了:SendSync是在线程间安全使用一个值的关键。

这一节我觉得了解到这里即可,因为通常情况下,你不需要去手动实现SendSync

通常并不需要手动实现 SendSync trait,因为由 SendSync 的类型组成的类型,自动就是 SendSync 的。
因为他们是标记 trait,甚至都不需要实现任何方法。他们只是用来加强并发相关的不可变性的。

如果你有兴趣,可以参考《Rust语言圣经(Rust Course) 为裸指针实现Send和Sync 这一小节

总结一下

  1. 实现了 Send 的类型,可以在线程间传递
  2. 实现了 Sync 的类型,可以在线程间共享
  3. 实现了 Sync 的类型,其引用的值必须实现 Send
updatedupdated2025-03-012025-03-01