使用Rust的Condvar定期唤醒线程遇到的异常问题
问题分析与修复
你的代码无法正常运行有两个核心问题:
子线程长期持有Mutex锁
子线程在loop外就获取了started的锁,整个循环过程中都没有释放锁。主线程调用cvar.wait(started)时,虽然会暂时释放锁,但被唤醒后需要重新获取锁才能继续执行——可子线程一直占着锁不放,主线程根本拿不到,自然无法打印内容。Condvar的使用不符合设计逻辑
Condvar的设计是基于共享状态变化来唤醒等待线程,你的代码里既不需要维护状态值,也没在唤醒后做状态重置,同时子线程持锁的行为彻底阻塞了主线程的锁获取路径。
修复后的代码(无需状态值的定期唤醒)
如果只是需要定期唤醒主线程,无需维护额外状态,可以这样调整:
use std::sync::{Arc, Mutex, Condvar}; use std::thread; fn main() { let pair = Arc::new((Mutex::new(()), Condvar::new())); let pair2 = Arc::clone(&pair); thread::spawn(move|| { let (_lock, cvar) = &*pair2; loop { // Condvar的notify操作不需要持有对应Mutex锁 cvar.notify_one(); thread::sleep(std::time::Duration::from_millis(20)); } }); let (lock, cvar) = &*pair; let mut guard = lock.lock().unwrap(); loop { // wait会自动释放锁,被唤醒后重新获取锁 guard = cvar.wait(guard).unwrap(); println!("done"); } }
关键调整点
- 子线程不再持有Mutex锁:因为不需要修改共享状态,直接调用
notify_one()即可,Condvar的通知操作本身不需要持有对应锁。 - 共享状态改为空元组
():既然不需要维护唤醒相关的状态,用空元组占位满足类型要求即可。 - 主线程保持标准wait逻辑:
wait方法会自动释放锁,被唤醒后重新获取锁,确保线程安全。
进阶优化(避免虚假唤醒)
如果需要确保只有被主动notify才执行逻辑(避免Condvar的虚假唤醒问题),可以配合一个简单的状态标记:
use std::sync::{Arc, Mutex, Condvar}; use std::thread; fn main() { let pair = Arc::new((Mutex::new(false), Condvar::new())); let pair2 = Arc::clone(&pair); thread::spawn(move|| { let (lock, cvar) = &*pair2; loop { let mut state = lock.lock().unwrap(); *state = true; cvar.notify_one(); drop(state); // 主动释放锁,让主线程能获取 thread::sleep(std::time::Duration::from_millis(20)); } }); let (lock, cvar) = &*pair; let mut started = lock.lock().unwrap(); loop { // 循环检查状态,过滤虚假唤醒 while !*started { started = cvar.wait(started).unwrap(); } println!("done"); *started = false; // 重置状态,等待下一次唤醒 } }
内容的提问来源于stack exchange,提问作者Poperton
相关产品推荐
相关产品推荐

