You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Rust中使用lazy_static+RwLock读写自定义结构体时读操作触发死锁

解决RwLock持有写锁时读操作触发死锁的问题

这个死锁问题其实很好定位——你在同一个线程里,握着写锁的同时又去抢读锁了!Rust标准库的RwLock不支持重入的,也就是说同一个线程不能在持有锁(不管是读还是写状态)的情况下,再次获取这把锁,否则就会触发死锁检测的panic,也就是你看到的那条错误信息。

咱们看你的代码:在auth函数里,你先通过LATEST_AUTH.write().unwrap()拿到了写锁,这个锁会一直被持有,直到au变量离开作用域。结果你紧接着就调用LATEST_AUTH.read().unwrap(),这相当于同一个线程在握着写锁的同时,又伸手去拿读锁,直接触发了死锁。

给你两个简单的解决办法:

办法一:直接用已持有的写锁访问数据

既然你已经拿到了写锁,那不管是读还是写数据都没问题,完全不需要再去申请读锁。直接用au这个引用读取就行:

pub fn auth(){
    let api_resp: ApiResponse = res.json().unwrap(); //From a reqwest res
    let mut au = LATEST_AUTH.write().unwrap();
    au.access_token = api_resp.access_token.clone();
    // 直接用au访问数据,不用再获取读锁
    println!("LATEST_AUTH access token: {}", au.access_token);
    // 如果想打印整个实例,可以给Authentication实现Debug后用{:?}
    // println!("LATEST_AUTH: {:?}", au);
}

办法二:先释放写锁,再获取读锁

如果确实需要在写操作完成后,重新通过读锁访问数据,那你可以手动缩小写锁的作用域,或者主动释放写锁:

pub fn auth(){
    let api_resp: ApiResponse = res.json().unwrap(); //From a reqwest res
    // 用花括号缩小写锁的作用域
    {
        let mut au = LATEST_AUTH.write().unwrap();
        au.access_token = api_resp.access_token.clone();
    } // 这里au离开作用域,写锁自动释放
    // 现在可以安全获取读锁了
    let auth_read = LATEST_AUTH.read().unwrap();
    println!("LATEST_AUTH: {}", auth_read);
}

或者手动调用drop释放写锁:

pub fn auth(){
    let api_resp: ApiResponse = res.json().unwrap(); //From a reqwest res
    let mut au = LATEST_AUTH.write().unwrap();
    au.access_token = api_resp.access_token.clone();
    drop(au); // 主动释放写锁
    let auth_read = LATEST_AUTH.read().unwrap();
    println!("LATEST_AUTH: {}", auth_read);
}

另外提一句,你用lazy_static初始化RwLock的方式是完全没问题的,核心问题就是锁的重入尝试。记住Rust标准库的RwLock没有重入能力,不管是读锁重复获取,还是写锁转读锁,同一个线程里这么做都会触发死锁panic哦。

内容的提问来源于stack exchange,提问作者Adrien Chapelet

火山引擎 最新活动