Rust Atomic 笔记
basic of rust concurrency 内部可变性是指可以通一个不可变引用来实现对内部数据的修改。标准库中有Cell<T>,Cell相当于持有这个Obj,要做修改只能先把T move出来再把T塞回去。RefCell<T>不但持有这个obj,还会维护一个引用计数,如果在运行时有多个Mut借用时,会panic。 RwLock是多线程版本的RefCell,但是多个可变借用时,或者说多个写时,会block住试图拿这个可变借用的线程,让它进入sleep。而Mutex不像Rwlock可以实现多个可读借用,mutex是彻底的独占的,其它线程都必须等待锁的释放。 UnSafeCell是实现内部可变性的关键类型,它只有get函数可以得到一个底层类型的raw pointer,UnsafeCell没有任何保证安全性的限制,需要用户自己来进行安全性的保证。一般不会直接用unsafe cell,都是包装成另一个类型来限制使用,比如实现成cell或者mutex。 Sendtrait意味着这个类型的所有权可以在线程之间转移,Synctrait意味着这个类型可以在线程之间共享。 一个struct如果所有的类型都是send和sync,那它本身也是send和sync的,如果要实现非send和sync的话,可以用phontomdata 标记类型。PhantomData用于标记Struct非sync,毕竟这是个zero sized type。 给一些非auto trait实现send和sync是unsafe的,因为取决于其中类型的具体实现,所以安全性需要自己来保证。 rust的mutex,实现了一个其它线程如果持有了mutex,但是panic了,这个mutex就是poison的机制:rust mutex doc。 mutex一些值得注意的点: if let的scope。 if let Some(item) = list.lock().unwrap().pop() {//会lock到这个if结束 process_item(item); } if list.lock().unwrap().pop() == Some(1) {//在进入body前,MutxGuard就已经drop掉了。 do_something(); } rust 2024做出的一个if let的小修改,即if let的表达式生命周期不会延长到else body中去:rust 2024 doc 大多数的读写锁的实现都会block新的读者出现,当写者等待时,因为可能会多个读者共享导致写者一直要等待,这是读写锁中一个经典的饥饿问题。 ## parking and condition variables 当一个线程在等待别的线程的通知的时候,这种叫thread parking,parking的时候,线程会进入睡眠,唤醒可以用unpark。 在实现上,unpark有一个很重要的特性是,有一个信号保留机制,即unpark在park之前执行了,这个park也不会让线程进入睡眠,因为消费者需要对生产者的每一个unpark都有响应,否则会有丢数据的风险。 但是unpark并不能叠加,所以unpark两次后,再Park两次还是会线程进入睡眠。 条件变量一般是与mutex结合使用,传入同一个mutex,一个线程wait,一个线程notify。如果两个线程在操作同一个条件变量但是不同的mutex,会引起panic. Atomic and Memory order 原子类型可以在多线程间安全的访问和修改,但是依赖于Memory Ordering。比如最简单的order,Ralex,Relax只能保证单个变量的一致性,而多个变量间顺序无法保证。 atomic i32的溢出是回环的,不像普通的i32,溢出是会panic的。 为什么需要memory order?CPU会为了优化而把程序中的指令进行重排,写的是什么顺序,执行起来可能是别的顺序。 Happens before即保证A发生在B之前,但是在多线程中却是不一定的,使用锁可以保证,或者更严格的memory ordering。 ...