赞
踩
问题:
编写如下代码的时候出现死锁:
- pub fn test_double_lock() {
- let t = Arc::new(Mutex::new(1));
-
- let t1 = t.clone();
- let t2 = t.clone();
-
- let h = std::thread::spawn(move || {
- println!("hello trace1");
- let l1 = t1.lock().unwrap();
- println!("hello trace2");
- let l2 = t2.lock().unwrap();
- println!("hello trace3");
- });
-
- h.join().unwrap();
- }
输出卡在了"hello trace2",这个原因主要是Rust的mutex是非recursive的 。所以同一个线程中如果出现2次获取锁,就会死锁掉。哈哈,网上查了一下,很多人都说recursive lock是垃圾代码,要修复。鉴于个人能力,很多这样的代码我都改不掉,所以我用Rust实现了一个recursive的锁。。。
主要数据结构
- struct TarMutexRecord {
- m_count:u32, //1
- m_ownerid:Option<ThreadId> //2
- }
-
- pub struct TarMutex {
- m_record:Mutex<TarMutexRecord>, //3
- m_cond:Condvar //4
- }
1.获取锁计数,如果计数到0的话,唤醒其他等待线程去拿锁
2.锁的owner,每次拿到锁以后记录拿锁线程,防止其他线程调用unlock释放
3.#1,#2的数据记录
4.如果锁的owner不是当前线程,则调用这个m_cond等待。当锁释放的时候,如果计数为0,唤醒m_cond等待的线程。
主要接口
- pub fn lock(&self) {
- let tid = std::thread::current().id();
- loop {
- let mut lock = self.m_record.lock().unwrap();
- if lock.m_count == 0 {
- //no owner
- lock.m_count += 1; //1
- lock.m_ownerid = Some(tid); //2
- return;
- } else if let Some(ownerid) = lock.m_ownerid{
- if ownerid == tid {
- lock.m_count += 1;//3
- return;
- }
- }
- self.m_cond.wait(lock);//4
- }
- }
-
- pub fn unlock(&self) {
- let tid = std::thread::current().id();
- let mut lock = self.m_record.lock().unwrap();
- if let Some(ownerid) = lock.m_ownerid{
- if ownerid == tid {
- lock.m_count -= 1;//5
- if lock.m_count == 0 {
- lock.m_ownerid = None;
- self.m_cond.notify_all();//6
- }
- }
- }
- }
1.如果当前线程没有获取过锁,则只要计数+1
2.记录当前获取到锁线程id
3.如果当前线程已经获取过锁,则只要计数+1
4.如果当前有其他线程正在拿锁,则等待
5.锁释放时先计数-1
6.如果当前计数为0,表明锁都已经释放,直接唤醒所有等待锁的队列。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。