MutexGuard: 数据加锁
MutexGuard与String、Box、Cow<’a, B>的对比
Deref+Drop
String、Box
- 它不但通过 Deref 提供良好的用户体验
- 还通过 Drop trait 来确保,使用到的内存以外的资源在退出 时进行释放。
使用Mutex::lock获取
MutexGuard这个结构是在调用 Mutex::lock 时生成的
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> { unsafe { self.inner.raw_lock(); MutexGuard::new(self) } }
- 首先,它会取得锁资源,如果拿不到,会在这里等待;
- 如果拿到了,会把 Mutex 结构的引 用传递给 MutexGuard。
定义与Deref、Drop trait实现
MutexGuard 的定义以及它的 Deref 和 Drop 的实现
// 这里用 must_use,当你得到了却不使用 MutexGuard 时会报警 #[must_use = "if unused the Mutex will immediately unlock"] pub struct MutexGuard<'a, T: ?Sized + 'a> { lock: &'a Mutex<T>, poison: poison::Guard, } impl<T: ?Sized> Deref for MutexGuard<'_, T> { type Target = T; fn deref(&self) -> &T { unsafe { &*self.lock.data.get() } } } impl<T: ?Sized> DerefMut for MutexGuard<'_, T> { fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.lock.data.get() } } } impl<T: ?Sized> Drop for MutexGuard<'_, T> { #[inline] fn drop(&mut self) { unsafe { self.lock.poison.done(&self.poison); self.lock.inner.raw_unlock(); } } }
从代码中可以看到:
- 当 MutexGuard 结束时,Mutex 会做 unlock
- 这样用户在使用 Mutex 时,可以不必关心何时释放这个互斥锁。
- 因为无论你在调用栈上怎样传递 MutexGuard ,哪怕在错误处理流程上提前退出,Rust 有所有权机制,可以确保只要 MutexGuard 离开作用域,锁就会被释放
使用Mutex_MutexGuard的例子
Mutex & MutexGuard example
use lazy_static::lazy_static; use std::borrow::Cow; use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; // lazy_static 宏可以生成复杂的 static 对象 lazy_static! { // 一般情况下 Mutex 和 Arc 一起在多线程环境下提供对共享内存的使用 // 如果你把 Mutex 声明成 static,其生命周期是静态的,不需要 Arc static ref METRICS: Mutex<HashMap<Cow<'static, str>, usize>> = Mutex::new(HashMap::new()); } fn main() { // 用 Arc 来提供并发环境下的共享所有权(使用引用计数) let metrics: Arc<Mutex<HashMap<Cow<'static, str>, usize>>> = Arc::new(Mutex::new(HashMap::new())); for _ in 0..32 { let m = metrics.clone(); thread::spawn(move || { let mut g = m.lock().unwrap(); // 此时只有拿到 MutexGuard 的线程可以访问 HashMap let data = &mut *g; // Cow 实现了很多数据结构的 From trait,所以我们可以用 "hello".into() 生成 Cow let entry = data.entry("hello".into()).or_insert(0); *entry += 1; // MutexGuard 被 Drop,锁被释放 }); } thread::sleep(Duration::from_millis(100)); println!("metrics: {:?}", metrics.lock().unwrap()); }
在解析 URL 的时候,我们经常需要将 querystring 中的参数,提取成 KV pair 来进一步使 用。 绝大多数语言中,提取出来的 KV 都是新的字符串,在每秒钟处理几十 k 甚至上百 k 请求的系统中,你可以想象这会带来多少次堆内存的分配。 但在 Rust 中,我们可以用 Cow 类型轻松高效处理它,在读取 URL 的过程中:
- 每解析出一个 key 或者 value,我们可以用一个 &str 指向 URL 中相应的位置,然后用 Cow 封装它
- 而当解析出来的内容不能直接使用,需要 decode 时,比如 “hello%20world”,我们 可以生成一个解析后的 String,同样用 Cow 封装它。
你可以把 MutexGuard 的引用传给另一个线程使用,但你无法把 MutexGuard 整个移动到另一个线程
use std::sync::Mutex; fn main() { // let m = Mutex::new(Mutex::new(1)); let g = m.lock().unwrap(); { rayon::join( || { let mut g1 = g.lock().unwrap(); *g1 += 1; println!("Thread 1: {:?}", *g1); }, || { let mut g1 = g.lock().unwrap(); *g1 += 1; println!("Thread 1: {:?}", *g1); }, ); } }
MutexGuard 的智能指针有很多用途
- r2d2类似实现一个数据库连接池:源码
#![allow(unused)] fn main() { impl<M> Drop for PooledConnection<M> where M: ManageConnection, { fn drop(&mut self) { self.pool.put_back(self.checkout, self.conn.take().unwrap()); } } impl<M> Deref for PooledConnection<M> where M: ManageConnection, { type Target = M::Connection; fn deref(&self) -> &M::Connection { &self.conn.as_ref().unwrap().conn } } impl<M> DerefMut for PooledConnection<M> where M: ManageConnection, { fn deref_mut(&mut self) -> &mut M::Connection { &mut self.conn.as_mut().unwrap().conn } } }
- 类似 MutexGuard 的智能指针有很多用途。比如要创建一个连接池,你可以在 Drop trait 中,回收 checkout 出来的连接,将其再放回连接池。