第四十九篇我们学习了在多线程中使用通信来实现并发,这一篇学习使用共享内存的方式实现并发。Rust支持通过共享状态来实现并发。Channel类似单所有权:一旦值的所有权转移至Channel,就无法使用它了。而共享内存并发类似于多所有权:多个线程可以同时访问同一块内存。使用Mutex来每次只允许一个线程来访问数据 Mutex是mutualexclusion(互斥锁)的缩写。在同一时刻,Mutex只允许一个线程来访问某些数据。想要访问数据:线程必须首先获得互斥锁(lock),lock数据结构是mutex的一部分,它能跟踪谁对数据拥有独占访问权mutex通常被描述为:通过锁定系统来保护它所持有的数据Mutex的两条规则在使用数据之前,必须尝试获取锁(lock)使用完mutex所保护的数据,必须对数据进行解锁,以便于其它线程可以获取锁Mutex的API 通过Mutex::new(数据)来创建Mutex,Mutex是一个智能指针。访问数据前,通过lock方法来获得锁:这个方法会阻塞当前线程lock可能会失败返回的是MutexGuard(智能指针,实现了Deref和Drop) 例:usestd::sync::{Mutex,MutexGuard};fnmain(){letmMutex::new(5);{letmutnum:MutexGuardi32m。lock()。unwrap();num6;由于MutexGuard实现了Deref,所以可以使用解引用}由于MutexGuard实现了Drop,所以离开作用域会被释放,这样就不用手动释放锁了。println!(m{:?},m)}多线程共享Mutex 例:usestd::{sync::{Mutex},thread};fnmain(){letcounterMutex::new(0);letmuthandlesvec!〔〕;forin0。。10{lethandlethread::spawn(move{letmutnumcounter。lock()。unwrap();num1;});handles。push(handle)}forhandleinhandles{handle。join();}println!(Result{},counter。lock()。unwrap())} 运行以上代码会报错,因为在第一个循环中,第一次进入循环时创建了线程,并把counter的所有权移动至闭包内了,那么剩下的循环就无法获得所有权了。多线程的多重所有权,使用Arc来进行原子引用计数 之前学过Rc可以使数据被多重引用,但是它只能在单线程下使用。Arc和Rc类似,它可以用于并发场景。A:atomic原子的。那么为什么所有的基础类型都不是原子的,为什么标准库类型不默认使用Arc。因为需要牺牲性能作为代价。Arc和Rc的API是相同的。 例:usestd::{sync::{Arc,Mutex},thread,};fnmain(){letcounterArc::new(Mutex::new(0));letmuthandlesvec!〔〕;forin0。。10{letcounterArc::clone(counter);lethandlethread::spawn(move{letmutnumcounter。lock()。unwrap();num1;});handles。push(handle)}forhandleinhandles{handle。join()。unwrap();}println!(Result{},counter。lock()。unwrap())} 运行代码输出10。RefCellRcvsMutexArch1 Mutex提供内部可变性,和Cell家族(比如RefCell)一样。我们使用RefCell来改变Rc里面的内容;同样使用Mutex来改变Arc里面的内容。注意:Mutex有死锁风险。Send和Synctrait Rust语言的并发特性比较少,目前学的并发特性都来自标准库(而不是语言本身)。所以我们无需局限于标准库的并发,可以自己实现并发。但在Rust语言中有两个并发的概念:td::marker::Sync和std::marker::Send这两个trait。Send允许线程间转移所有权 实现Sendtrait的类型可以在线程间转移所有权。Rust中几乎所有类型都实现了Send,但是Rc没有实现,所以它只能用于单线程。任何完全由实现了Send的类型组成的类型也被标记为Send。除了原始指针之外,几乎所有基础类型都实现了Send。Sync允许从多线程访问 实现了Sync的类型可以安全的被多个线程引用。也就是说,如果T是Sync,那么T就是Send,即引用可以被安全的送往另一个线程。基础类型都是Sync,完全由Sync类型组成的类型也是Sync,但Rc不是Sync的;RefCell和Cell家族也不是Sync的。不过Mutex是Sync的。 通常并不需要手动实现Send和Synctrait,因为由Send和Sync的类型组成的类型,自动就是Send和Sync的。因为他们是标记trait,甚至都不需要实现任何方法。他们只是用来加强并发相关的不可变性的。 手动实现这些标记trait涉及到编写不安全的Rust代码,需要十分以及特别的小心。