C++中条件变量与互斥锁的工作机制及代码无输出问题排查
条件变量与互斥锁的工作机制及代码问题分析
一、条件变量与互斥锁的基本工作逻辑
- 互斥锁(mutex):用于保护共享资源,确保同一时间仅一个线程能访问被锁资源,避免数据竞争。
- 条件变量(condition_variable):实现线程间同步,让线程等待特定条件满足后再执行,避免无意义的轮询等待。
- 结合使用的核心流程:
- 线程先获取互斥锁,锁定共享资源。
- 检查目标条件:若不满足,调用
wait()方法,此时互斥锁会被自动解锁,线程进入阻塞等待状态。 - 当其他线程通过
notify_one()或notify_all()唤醒等待线程时,被唤醒的线程会重新自动获取互斥锁,再次检查条件(防止虚假唤醒),条件满足后继续执行后续代码。
二、关于“wait时互斥锁是否解锁”的明确回答
是的。调用condition_variable::wait(unique_lock<mutex>&)时,会自动执行两个操作:
- 解锁传入的互斥锁,允许其他线程获取锁访问资源。
- 将当前线程挂起,进入等待队列,直到被通知唤醒。
线程被唤醒后,wait()会重新尝试获取互斥锁,成功获取后才会返回,继续执行后续代码。
三、你的代码无输出的原因分析
先贴出你的代码:
#include <thread> #include <mutex> #include <condition_variable> #include <iostream> #include <chrono> using namespace std; using namespace std::chrono_literals; condition_variable cv; mutex mu; void thread1() { mu.lock(); unique_lock lck(mu); cv.wait(lck); cout << 1; } void thread2() { this_thread::sleep_for(1s); mu.lock(); cv.notify_all(); cout << 2; } int main() { thread t1(thread1); thread t2(thread2); this_thread::sleep_for(2s); }
问题出在以下几点:
- thread1中的锁重复锁定:
你先手动调用mu.lock()锁住互斥锁,之后又用unique_lock lck(mu)尝试再次获取该锁。unique_lock的默认构造(传入mutex参数)会尝试锁定mutex,此时mutex已被手动锁住,导致unique_lock构造阻塞,永远到不了cv.wait(lck)步骤。 - thread2无法获取锁:
thread1通过mu.lock()占用锁后,thread2执行到mu.lock()时会被阻塞,永远无法执行notify_all()和输出操作。 - 主线程提前退出:
主线程仅sleep2秒,此时t1和t2都处于阻塞状态,主线程退出会直接终止整个进程,导致两个线程的代码都没机会完成输出。
四、修复后的代码示例
#include <thread> #include <mutex> #include <condition_variable> #include <iostream> #include <chrono> using namespace std; using namespace std::chrono_literals; condition_variable cv; mutex mu; void thread1() { unique_lock<mutex> lck(mu); // 用unique_lock自动管理锁,禁止手动lock cv.wait(lck); cout << 1 << endl; } void thread2() { this_thread::sleep_for(1s); { lock_guard<mutex> lck(mu); // 用lock_guard自动管理锁的生命周期 cv.notify_all(); cout << 2 << endl; } } int main() { thread t1(thread1); thread t2(thread2); t1.join(); // 等待t1执行完成 t2.join(); // 等待t2执行完成 }
修复要点:
- 禁止手动调用
mutex::lock()后再用unique_lock或lock_guard管理同一个锁,应让RAII类(unique_lock/lock_guard)自动处理锁的获取与释放,避免死锁或重复锁定问题。 - 主线程需用
join()等待子线程完成,而非单纯sleep,确保子线程有足够时间执行完毕。 - thread2中用
lock_guard临时获取锁,通知完成后自动释放,避免长期占用锁。
内容的提问来源于stack exchange,提问作者zhzhy
相关产品推荐
相关产品推荐

