P.MTH.LCK.01 多线程下要注意识别锁争用的情况,避免死锁

【描述】

Rust 并不能保证没有死锁,要注意 LockResult<MutexGuard<'_, T>> 的生命周期,以防止出现死锁的情况。

【反例】

下面代码有一定的几率会触发死锁。

// 触发死锁时,只会输出:
// Thread 1 holds a lock and starts waiting b lock
// Thread 2 hodls a lock and starts waiting a lock

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let a = Arc::new(Mutex::new(0));
    let b = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    {
        let a = Arc::clone(&a);
        let b = Arc::clone(&b);
        let handle = thread::spawn(move || {
            let mut a_num = a.lock().unwrap();
            *a_num += 1;
            println!("Thread 1 holds a lock and starts waiting b lock");
            let mut b_num = b.lock().unwrap();
            *b_num += 1;
        });
        handles.push(handle);
    }
    {
        let a = Arc::clone(&a);
        let b = Arc::clone(&b);
        let handle = thread::spawn(move || {
            let mut b_num = b.lock().unwrap();
            *b_num += 1;
            println!("Thread 2 holds b lock and starts waiting a lock");
            let mut a_num = a.lock().unwrap();
            *a_num += 1;
            println!("Thread 2");
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }

    println!("Done {}", *a.lock().unwrap()); // never reach here
}

【正例】

// 无死锁发生,正常输出
// Thread 1 holds a lock and starts waiting b lock
// Thread 2 hodls a lock and starts waiting a lock
// Thread 2
// Done 2

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let a = Arc::new(Mutex::new(0));
    let b = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    {
        let a = Arc::clone(&a);
        let b = Arc::clone(&b);
        let handle = thread::spawn(move || {
            {   // <- 这里增加显示作用域,确保 lock 之后可以自动解锁
                // 即 LockResult<MutexGuard<'_, T>> 在作用域之外会自动释放
                let mut a_num = a.lock().unwrap();
                *a_num += 1;
                println!("Thread 1 holds a lock and starts waiting b lock");
            }
            {   // <- 这里增加显示作用域,确保 lock 之后可以自动解锁
                // 即 LockResult<MutexGuard<'_, T>> 在作用域之外会自动释放
                let mut b_num = b.lock().unwrap();
                *b_num += 1;
            }
        });
        handles.push(handle);
    }
    {
        let a = Arc::clone(&a);
        let b = Arc::clone(&b);
        let handle = thread::spawn(move || {
            {   // <- 这里增加显示作用域,确保 lock 之后可以自动解锁
                // 即 LockResult<MutexGuard<'_, T>> 在作用域之外会自动释放
                let mut b_num = b.lock().unwrap();
                *b_num += 1;
                println!("Thread 2 holds b lock and starts waiting a lock");
            }
            {   // <- 这里增加显示作用域,确保 lock 之后可以自动解锁
                // 即 LockResult<MutexGuard<'_, T>> 在作用域之外会自动释放
                let mut a_num = a.lock().unwrap();
                *a_num += 1;
            }
            println!("Thread 2");
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }

    println!("Done {}", *a.lock().unwrap());
}