使用条件变量实现资源有限访问计数:如何避免死锁?
修复多线程资源访问控制中的偶发死锁问题
你的代码出现死锁的核心原因是条件变量与保护共享状态的互斥锁不匹配,同时存在双重锁导致的竞态间隙:
- 你用
mutex_counter保护共享变量num_accesses,但条件变量cv_wait却绑定了另一个互斥锁mutex_wait,违反了条件变量的使用规则——条件变量必须和保护共享数据的锁配对使用,否则无法保证判断条件与等待操作的原子性。 - 在
acquire中,从释放mutex_counter到获取mutex_wait并进入wait的间隙,其他线程可能已经将num_accesses降到0,但当前线程已经错过了唤醒信号,最终进入无限等待。
修正方案
只需要用一个互斥锁同时保护num_accesses和配合条件变量,确保条件判断与等待操作的原子性:
#include <mutex> #include <condition_variable> #include <cassert> std::mutex mutex_counter; std::condition_variable cv_wait; std::size_t num_accesses = 0; std::size_t max_accesses = 8; bool acquire() { std::unique_lock<std::mutex> lock(mutex_counter); // 循环判断条件,避免虚假唤醒 while (num_accesses >= max_accesses) { cv_wait.wait(lock); // wait会自动释放锁,被唤醒后重新获取锁 } ++num_accesses; return true; } bool release() { std::lock_guard<std::mutex> lock(mutex_counter); assert(num_accesses > 0); --num_accesses; cv_wait.notify_all(); // 唤醒所有等待的线程 return true; }
关键修改点
- 移除多余的
mutex_wait,仅保留保护共享状态的mutex_counter,让条件变量与它绑定。 acquire中使用unique_lock(因为wait需要解锁/重新加锁的能力),循环判断num_accesses >= max_accesses,确保只有当条件满足时才继续执行。- 条件变量的
wait方法会自动释放锁,让其他线程可以修改num_accesses;被唤醒后会重新获取锁,再次检查条件(避免虚假唤醒)。
这样就彻底消除了竞态间隙,不会出现无人唤醒的情况。
内容的提问来源于stack exchange,提问作者Caduchon
相关产品推荐
相关产品推荐

