Rust Atomics and Locks 阅读笔记 第一章 Rust并发基础

Rust Atomic and Locks 原书信息

作者:Mara Bos
出版社: O’REILLY
ISBN:978-1-098-11944-7

第一章 Rust并发基础

  1. 在多核处理器出现之前,操作系统通过在进程间快速切换,每个进程运行一段时间,来实现并发(concurrency);多核处理器出现后,实现了真正的并行(parallel);
  2. 不同的进程之间相互隔离,无法访问对方的内存,除非通过操作系统内核。但是进程可以产生(spawn)线程(threads),线程之间共享内存空间,从而可以简单地实现相互通信;
  3. 在Rust中,通过标准库的std::thread::spawn来产生新的线程,该方法需要一个参数,参数是该线程用于执行的函数(function),可以通过thread::current()::id()来获取当前线程的ID,该ID仅能用于比较和复制,并且无法保证生成的ID是连续的;
  4. main函数退出时会结束整个程序,即使有其他spawned的线程还在运行,如果需要所有线程都结束后再退出main函数,需要将let t = thread::spawn(f)的返回值(是一个JoinHandle类型)通过t.join().unwrap()来回收;
  5. println!宏使用了std::io::Stdout::lock()锁来确保每一次打印不被其他线程的打印干扰;
  6. spawn函数有一个'static静态生命周期限制,要求spawn的函数必须在整个程序(program)运行时存活,正常通过fn function()声明的函数没有问题,但是闭包(closure)通过引用捕获(capturing by reference)局部变量的时候,这个闭包可能不会永久存活,因为当局部变量销毁时,闭包捕获的引用也将失效。详见原书page4;
  7. std::thread::spawn实际上是std::thread::Builder::new().spawn().unwrap()的缩写,可以通过std::thread::Builder来定制化产生的线程,例如分配堆栈空间、给thread取一个名字(std::thread::current().name());
  8. 如果想要spawn一个线程来借用局部变量同时避免声明周期检查的话,可以通过std::thread::scope来声明一个有限范围的线程,只要确定scope的周期小于局部变量的生命周期,就可以安全地在scope范围内spawn函数来借用局部变量;详见page5;
  9. 通过static关键字来声明静态变量,该变量的生命周期是整个程序,其他的线程可以access该变量但不能own这个变量,因为它是被整个程序(entire program)拥有(own);
  10. 通过Box::leak来释放指针的所有权,保证永远不drop,从而使得该指针拥有静态生命周期,可以被所有的线程借用;
  11. 'static生命周期不一定表示该值在程序一开始就存在,但一定会存活到程序结束为止;
  12. std::rc::RcBox类似,但是带有引用计数(reference counted),它的clone的结果是同一个指针,与Box的clone不一样;Rc并非线程安全,因此不能send到别的线程中,可以使用Arc,它是Rc的线程安全版本;
  13. Rust允许并建议定义新的引用变量同时使用旧变量的变量名,称为shadow variable,通过新建一个作用域(scope)来实现,详见Page10;
  14. 不可变借用(&T)可以copy,可变借用会保证它是对数据唯一活跃的借用;
  15. 除非使用了unsafe,否则不可能写出通过rust编译器安全检查的非安全代码;
  16. 内部可变性只在共享借用(shared borrowing)中允许可变性(mutation),如果是独有借用(exclusive borrowing),依然能保证没有其他的借用存在,当然unsafe code例外,因为它允许多个可变借用同时存在;
  17. std::cell::Cell<T>仅仅包裹(wraps)一个T类型,在共享借用的情况下允许内部可变,但仅仅允许使用者把里面的值复制出来(copy out)或者取出(.take())以及完整替换(replace as a whole),而且仅能在单线程中使用,不支持多线程;
  18. std::cell::RefCell::<T>与Cell的区别在于RefCell可以借用(.borrow())T的内容(content),有一点小小的运行时花费,同时RefCell能够追踪所有的借用,如果在T已经被可变借用(.borrow_mut())的情况下再去借用,程序会panic,反之亦然;与Cell一样,RefCell也只能在单线程中使用;
  19. MutexRwLock是RefCell的并发版本,意味着他们支持多线程使用,与RefCell不同的地方在于,RefCell检测到互斥的借用(如可变借用和不可变借用同时出现)会panic,而Mutex和RwLock会等待(或者说sleep),直到互斥条件接触时继续下一步,RwLock()允许可变和不可变借用,而Mutex仅支持独有借用;
  20. Atomic类型是Cell的并发版本,但是不像Cell是模板类,Atomic没有Atomic<T>类型,而是针对内置类型一个个实现,例如AtomicBool
  21. UnsafeCell是内部可变性的源语(primitive),像CellMutex等等提供安全内部可变性的类型都是通过UnsafeCell实现的,UnsafeCell内部包裹一个裸指针(raw pointer);
  22. 一个类型可以Send表示它的所有权可以转移到别的线程中,一个类型可以Sync表示它的共享引用(shared reference,&T)可以转移到别的线程中(也就是send);
  23. 所有的内置类型例如i32, bool, str都是SendSync的,如果一个新的类型里的成员都实现了SendSync,那么这个类型也自动实现了这两个trait;如果想破解这两个自动实现的trait,可以使用std::marker::PhantomData<T>往struct里添加一个没有实现Send和Sync的成员变量,例如Cell<T>,详见page16;
  24. 裸指针(*const T, &mut T)没有实现SendSync,如果需要实现这两个trait,需要使用unsafe关键字;
  25. Mutex<T>通过lock()方法来获取锁,得到一个MutexGuard类型,保证已经获得了这个锁从而对其保护的内容进行独有修改,通过Drop()方法来解锁,into_inner()方法把Mutex的所有权夺走,使之不再被任何线程所引用,不再能进行locking操作;
  26. 如果某个获取了锁的线程panic了,会释放这个锁,但是当其他线程尝试用.lock()来获取锁的时候获取一个PoisonError,表示这个Mutex已经中毒(poisoned),可以把poisoned状态传递给所有试图获取这个锁的users;
  27. !不给.lock()返回的结果用let绑定变量也许写起来很方便,但是会容易走进一些不容易察觉的陷阱,当用match, if let, while let等语句来绑定等号右值的lock()时,会把锁持有直到{code block}全部执行完才释放,而不是if let语句结束时,详见Page21;
  28. Mutex<T>和RwLock<T>都要求T实现Send,但是RwLock额外要求T实现Sync
  29. 与C或者C++语言里的mutex不同,Rust的Mutex包含了要保护的数据,如果只是单纯的需要一个锁的话,可以使用Mutex<()>或者自己定义一个zero-sized类型来保护临界区;
  30. 通过std::thread::park()来暂停一个线程,当满足某些条件后通过unpark()来唤醒线程,例子详见Page24;
  31. !如果某个线程park()之前收到了一个unpark()消息,这个消息不会丢失,而是记录下来,下次收到了park()消息的时候会迅速unpark()来运行后面的代码;但是unpark()只会被记录一次,没有park()的线程收到了2次unpark()只会记录一次;park()机制比较适合1 consumer 1 producer场景;
  32. 条件变量std::sync::Condvar可以用于处理多个consumer需要被notify的情景,notify_one用于唤醒一个等待的线程,notify_all用于唤醒所有等待某个条件的线程;注意wait()方法把Unlocking, waiting, relocking操作集于同一个函数内,并且保证是原子操作,例子详见Page27,通常Condvar只搭配单个Mutex
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值