线程之间共享数据,即go中部分人所憎恶的方式!
然而,这个方式并非没有其优点,否则操作系统也不提供这种实现方式。
闲言少序,上正文!
一、概述
* 1.当我们大谈基于信道通信时,应该指的是应用级别。如果是操作系统,应该还是会有多种措施的,否则rust的源头在哪里来着。 * 2.rust共享内存,主要借助于Arc指针实现。所谓Arc,是Atomically Reference Counted的缩写,即原子引用计数。 它和Rc的主要 * 区别在于,Arc允许多线程同时访问,而Rc不允许。 * 3.此外还需要借助于Mutex(mutual exclusion 互相排斥之意),即互斥锁,来保证同一时刻只有一个线程可以访问共享资源。 * 4.Mutex的主要方法有lock(),lock用于获取锁,如果成功返回一个MutexGuard智能指针。获取锁的时候,会阻塞线程 * 5.MutexGuard智能指针实现了Deref和Drop,因此可以被当作原始数据的引用,同时Drop确保了当MutexGuard离开作用域时,锁会被释放。 * 6.Mutex 是一个类似RefCell的智能指针,都提供了内部可变性 * 7.Mutex<T> 也有造成 死锁(deadlock)的风险:当某个操作需要锁住两个资源,而两个线程分别持有两个资源的其中一个锁时,它们会永远相互等待 * 8.对于简单的数值运算,标准库中 std::sync::atomic 模块 提供了比 Mutex<T> 更简单的类型。针对基本类型,这些类型提供了安全、并发、原子的操作 * 例如 AtomicI32 Mutex的结构代码#[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "Mutex")] pub struct Mutex<T: ?Sized> { inner: sys::Mutex, poison: poison::Flag, data: UnsafeCell<T>, }
这个sys::Mutex是一个原子:
pub struct Mutex { futex: Atomic, }用于持有锁position(位置标记/占有标记)是一个原子布尔,用于表示是否被占有data就是原始的数据了。 Mutex最常用的方法lock用于获得锁,并返回如下:
#[stable(feature = "rust1", since = "1.0.0")] pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> { unsafe { self.inner.lock(); MutexGuard::new(self) } }这段代码的大概意思:调用操作系统的内部锁,返回一个新的MutexGuard。完整的结果就是包含一个锁和一个结果
二、示例-来自于书本的
use std::sync::{Arc, Mutex}; use std::thread; fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let c = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = c.lock().unwrap(); *num += 1; println!("线程{:?}正在执行,得到{}", thread::current().id(),*num); }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Result: {}", *counter.lock().unwrap()); }
借用Arc指针(多线程使用的,可以共享数据)和Mutex锁实现简单的数字自增游戏! 很像餐桌上大伙共享一个公勺,有了公勺你要干什么都可以,但一个时刻只有一把公勺!
三、示例-特意构建的死锁
根据书本的要求,我特意构建一个死锁的代码。
发生死锁的最主要原因是互相等待对方持有的资源,好比劫匪持有人质,警察要人质,常常会陷入死循环!直到一方崩溃
use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; fn main() { let 人质 = Arc::new(Mutex::new(5)); let 自由 = Arc::new(Mutex::new(10)); let h0=Arc::clone(&人质); let w0=Arc::clone(&自由); let 劫匪=thread::spawn(move || { //线程0先获得人质资源,然后等待一段时间再获得自由资源。 let mut _heigth = h0.lock().unwrap(); thread::sleep(Duration::from_millis(10)); println!("我已经持有人质,快点让路!"); let mut _自由 = w0.lock().unwrap(); let area = (*_heigth) * (*_自由); println!("线程{:?}正在执行,得到{}", thread::current().id(), area); }); let h1=Arc::clone(&人质); let w1=Arc::clone(&自由); let 警察=thread::spawn(move || { //线程1先获得的自由资源,然后等待一段时间再获得人质资源。 let mut _自由 = w1.lock().unwrap(); thread::sleep(Duration::from_millis(10)); println!("赶紧放了人质,否则爆你狗头!"); let mut _heigth = h1.lock().unwrap(); let area = (*_heigth) * (*_自由); println!("线程{:?}正在执行,得到{}", thread::current().id(), area); }); 劫匪.join().unwrap(); 警察.join().unwrap(); println!("众人在围观...."); let _heigth = 人质.lock().unwrap(); let _自由 = 自由.lock().unwrap(); let area = (*_heigth) * (*_自由); println!("Result: {}", area); }
四、小结
- rust使用Arc替代Rc指针,以便在多线程下的共有安全
- rust使用Mutex互斥锁,保证独享资源
- 正确编写代码,避免竞争死锁
- 避免死锁,必须遵循一些原则,例如
- 尽量减少一个线程同时持有多个锁的情况。如果一个线程确实需要持有多个锁,应该确保所有锁都按照相同的顺序被获取和释放
- 使用尝试锁(TryLock)
- 设置锁超时
- 检测和恢复死锁
避免死锁的方法,有多种,这里只能列出其中一二比较简单有效的实践。
标签:线程,unwrap,lock,并发,Arc,Mutex,let,rust From: https://www.cnblogs.com/lzfhope/p/18664452