为何在condition_variable.wait_for前调用lock.unlock会导致线程阻塞?
定时器类中condition_variable.wait_for()前手动解锁导致线程阻塞的问题
我用观察者模式实现了一个简易定时器类,遇到一个特殊问题:为什么在condition_variable.wait_for()之前调用lock.unlock(),会导致下一次循环创建std::unique_lock<std::mutex>时线程发生阻塞?
相关定时器类代码如下:
class LittleTimer { public: using ObserverData = std::tuple< std::chrono::steady_clock::time_point, std::function<void()> >; LittleTimer() : running_(false) {} ~LittleTimer() { Stop(); } void AddObserver(std::chrono::milliseconds interval, std::function<void()> callback) { std::unique_lock<std::mutex> lock(mutex_); auto scheduled_time = std::chrono::steady_clock::now() + interval; observers_.push(ObserverData(scheduled_time, std::move(callback))); lock.unlock(); condition_.notify_one(); } void Start() { if (!running_) { running_ = true; thread_ = std::thread([this]() { while (running_) { std::unique_lock<std::mutex> lock(mutex_); if(!observers_.empty()) { auto next_observer = observers_.top(); auto now = std::chrono::steady_clock::now(); if(std::get< 0 >(next_observer) <= now) { // 检查顶部事件的调度时间 auto observer = observers_.top(); observers_.pop(); lock.unlock(); // 调用观察者的回调 auto callback = std::get< 1 >(observer); callback(); } else { auto sleep_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::get< 0 >(next_observer) - now); // lock.unlock(); // !!!!!!!!!!!!!! 启用这行后,下一次循环会在创建lock时阻塞 condition_.wait_for(lock, sleep_time); } } else { condition_.wait_for(lock, std::chrono::milliseconds(10)); } } }); } } void Stop() { if (running_) { running_ = false; condition_.notify_one(); // 唤醒线程使其退出 if (thread_.joinable()) { thread_.join(); } } } private: struct ObserverComparator { bool operator()(const ObserverData& left, const ObserverData& right) const { return std::get< 0 >(left) > std::get< 0 >(right); } }; // 数据 std::priority_queue< ObserverData, std::vector< ObserverData >, ObserverComparator > observers_; // 同步组件 std::thread thread_; std::mutex mutex_; std::condition_variable condition_; // 优雅关闭标记 bool running_; };
移除上述代码中标记的解锁语句后,代码可正常运行。测试代码如下:
class Observer { public: virtual void OnTimerTick() = 0; }; class TimerObserver : public Observer { public: TimerObserver(const std::string& name) : name_(name), ticks_(0) {} void OnTimerTick() override { std::cout << "Observer " << name_ << " notified\n"; } private: std::string name_; int ticks_; }; int main() { LittleTimer timer; TimerObserver observer1("uno"); TimerObserver observer2("due"); timer.AddObserver(std::chrono::milliseconds(60), [&observer1]() { observer1.OnTimerTick(); }); timer.Start(); std::this_thread::sleep_for(std::chrono::milliseconds(20)); timer.AddObserver(std::chrono::milliseconds(10), [&observer2]() { observer2.OnTimerTick(); }); std::this_thread::sleep_for(std::chrono::milliseconds(60)); timer.Stop(); return 0; }
问题原因分析
std::condition_variable的所有wait系列函数(包括wait_for())有严格的使用规则:调用时必须持有传入的unique_lock,核心逻辑是:
wait_for()内部会自动释放锁,让其他线程可以访问共享资源(比如AddObserver中修改observers_);当等待结束(超时或被notify唤醒),函数会自动重新获取锁,确保后续操作共享数据的安全性。- 如果你在调用
wait_for()前手动解锁,就违反了这个规则,属于未定义行为。此时wait_for()操作的是一个未锁定的锁,会导致锁的状态混乱:比如等待结束后,函数尝试重新加锁,但锁可能已经被其他线程占用,或者之前的手动解锁操作导致锁的内部状态异常,最终在下一次循环创建unique_lock时,线程会阻塞在锁的获取步骤上。
正确用法总结
永远不要在调用condition_variable的wait系列函数前手动解锁传入的unique_lock,让函数内部自行处理锁的释放与重新获取,这是condition_variable设计的核心逻辑,也是保证线程安全的关键。
内容的提问来源于stack exchange,提问作者Budziwoj Man
相关产品推荐
相关产品推荐

