024 Rust死灵书之Send和Sync

Rust
339
0
0
2022-04-12

介绍

本系列录制的视频主要放在B站上Rust死灵书学习视频

Rust 死灵书相关的源码资料在github.com/anonymousGiga/Rustonomi...

Send和Sync

当同一块内存有多个别名,同时还可以改变内存的值的时候,它们就不是线程安全的。

Rust中根据Send和Sync trait获取相关的信息:

1、如果一个类型可以安全地传递给另一个线程,那么这个类型是实现了Send这个trait了的;

2、如果一个类型可以安全地被多个线程共享,那么这个类型就是Sync的。

Send和Sync是Rust并发机制的基础,但是它们是非安全的trait。Send和Sync是标志trait(即没有任何关联方法),类型要实现它们其实就是满足它们需要的内部特征。不正确的实现Send和Sync会导致未定义行为。

Send和Sync是自动推到的trait,规则:

1、如果一个类型完全由Send或者Sync组成,那么这个类型本身也是Send或者Sync的;

2、几乎所有的基本类型都是SendSync的。

例外情况:

1、裸指针不是Send的,也不是Sync的;

2、UnsafeCell不是Sync的(Cell和RefCell也不是);

3、Rc不是SendSync的(引用计数是共享且非同步的)。

Rc和UnsafeCell是典型的非线程安全的,因为它们允许非同步地共享可变状态。

不能自动推导的类型也可以很容易的实现Send和Sync:

struct MyBox(*mut u8);

unsafe impl Send for MyBox {}
unsafe impl Sync for MyBox {}

还有一个比较少见的场景,一个类型被自动推导为Send或者Sync,但它其实不满足二者的要求,此时我们可以为其去掉Send和Sync的实现,方式如下:

#![feature(negative_impls)]

// I have some magic semantics for some synchronization primitive!
struct SpecialThreadToken(u8);

impl !Send for SpecialThreadToken {}
impl !Sync for SpecialThreadToken {}

这里需要注意的是,一种类型自己不可能被不正确的推导为Send和Sync,只有类型和其它非安全代码一起实现一些特殊行为时,才可能成为一个不正确的Send或者Sync