您现在的位置是:首页 >技术教程 >Rust Atomics and Locks 阅读笔记 第二章 Atomics网站首页技术教程
Rust Atomics and Locks 阅读笔记 第二章 Atomics
简介Rust Atomics and Locks 阅读笔记 第二章 Atomics
- 原子操作(atomic operations)是多线程实现的基石,互斥锁(mutex)和条件变量(condition variable)都是通过原子操作来实现;
std::sync::atomic
包括了rust的内置原子操作类型(如AtomicI32, AtomicUsize等),大部分依赖于硬件架构和操作系统来实现,几乎所有的平台(platform)都提供了至少在指针(pointer)级别的原子类型;- 每个原子操作都有一个参数
std::sync::atomic::Ordering
,决定了我们想要这些原子操作内部进行的相对顺序,最宽松的顺序是Relaxed
,在线程A里先对变量X做了写操作,然后迅速对变量Y做写操作,在Relaxed的顺序下,某线程B可能看到的顺序是先改变Y再改变X; load(&self, ordering: Ordering), store(&self)
的第一个参数表示是一个共享引用(shared reference)&T而不是互斥引用(exclusive reference)&mut T,但是仍然可以改变T的值;- Fetch-and-Modify操作把获取-更改作为一个原子操作,返回更改前的值;同时要注意
fetch_add() 和 fetch_sub()
实现了溢出行为(wrapping behaviour),具体实现详见Page37; - !Page38给出了一个经典的move语义的编译器检查问题,
for t in 0..4
中t的生命周期与后面spawn的线程的生命周期的冲突问题,注意atomic
类型没有实现Copy特征,但是实现了Sync,表示它的共享引用&T可以在多线程中传递; - !Page41给出了一个生成唯一ID的用例,如何确保每次生成的ID唯一并且不溢出,第一个方案是当ID超过某个数字时使之Panic,通过
assert
,或者std::process::abort
停掉该进程,第二个方案是同时用fetch_sub
来减少ID的数值,第三个方案使用了compare-and-exchange
操作;这里第一个方案正好是标准库中Arc::clone()
用来处理borrow个数的方法,第二个方案是thread::scope
用来处理一个scope中同时有多少个线程的ID的方法; compare_exchange(&self, expected:i32, new:i32, success_order: Ordering, failure_order:: Ordering)
的签名比较复杂,但是可以用来实现所有其他的原子操作,Page43给了一个用compare_exchange来实现fetch_add的实例;- 当ABA problem出现的时候,使用
compare_exchange_weak()
会更加有效,及时当前的值与expected相等,也有可能返回Err,这种情况是为了应对可能出现的欺骗性compare-and-exchange(也就是ABA问题,在load完成后的很短时间内,修改atomic的值后又修改回来,企图欺骗编译原值并没有变化,但实际上进行过变化); fetch_update()
方法等同于load()
之后使用一个loop计算更新值然后调用compare_exchange_weak
来确保update的正确性,使用方法见Page45;- Page46给出了一个情景使用
compare_exchange()
比使用compare_exchnage_weak()
更高效; - 使用
std::sync::Once
和std::sync::OnceLock
可以解决单例中generate_random_key()
耗时过长但又不想重复调用的场景,确保某个静态变量只会初始化一次;
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。