锁相关的 API
在介绍特定的锁类型之前, 我们先针对它们的公共特性做一些说明.
了解这些之后, 接下来学习各种锁的用法时会更容易.
锁的底层接口
Lock::acquire()
, 本线程尝试获得锁, 如果获取失败, 本线程将被阻塞, 直到得到锁Lock::try_acquire()
, 本线程尝试获得锁, 如果失败, 就直接返回Lock::release()
, 释放锁
这种底层接口存在的问题是:
- 使用者只有在获得锁之后才能访问被锁保护的资源
- 锁定和解锁要成对使用, 不能出现忘记解锁的情况
锁的上层接口
针对上面提到的锁的底层接口存在的问题, 社区总结出了锁的上层接口:
- 锁定和解锁自动匹配
- 锁和它保护的资源紧密关联
这种接口风格来自于 C++ 中的 RAII :
- 使用
LockGuard<T>
类自动解锁Lock::acquire()
函数会返回LockGuard<T>
对象- 当
LockGuard<T>
对象超出作用域后, 编译器生成的代码自动调用Lock::release()
解锁
- 将锁与它保护的资源关联起来, 通过泛型类
Lock<T>
, 这样的话只有获得锁之后才能访问被保护的资源
这套接口更加安全易用.
看下面的代码示例:
use std::sync::Mutex; use std::thread; fn main() { let counter: Mutex<i32> = Mutex::new(42); let num_threads = 10; thread::scope(|s| { for _i in 0..num_threads { s.spawn(|| { if let Ok(mut guard) = counter.lock() { *guard += 1; } }); } }); if let Ok(result) = counter.into_inner() { println!("counter is: {result}"); assert_eq!(result, 52); } }
有两点要重点关注:
let counter: Mutex<i32> = Mutex::new(42);
这里的counter
是Mutex<i32>
类型, 这把锁保护的数据类型是i32
if let Ok(guard) = counter.lock()
,Mutex::lock()
接口返回的就是LockGuard<T>
类型- 当锁定成功后, 就返回
Ok(lock_guard)
, 这个对象在超出作用域后, 会自动被销毁, 自动解锁 - 当锁定失败时, 就返回
Err(lock_failed)
- 当锁定成功后, 就返回