面试题答案
一键面试条件变量和互斥锁协同工作机制
- 互斥锁(mutex):用于保护共享资源,确保同一时间只有一个线程可以访问共享资源,避免数据竞争。
- 条件变量(condition_variable):用于线程间的同步,当某个条件满足时,通知等待在条件变量上的线程。
- 协同工作过程:
- 线程首先获取互斥锁,进入临界区,检查共享资源的状态(条件)。
- 如果条件不满足,线程调用条件变量的等待函数(如
cv.wait
),这个过程中,线程会自动释放持有的互斥锁,并进入等待状态。 - 当另一个线程修改了共享资源的状态,使其满足条件时,该线程获取互斥锁,然后调用条件变量的通知函数(如
cv.notify_one
或cv.notify_all
),唤醒一个或所有等待在条件变量上的线程。 - 被唤醒的线程重新获取互斥锁,重新进入临界区,再次检查条件是否满足(因为可能存在虚假唤醒)。
生产者 - 消费者模型中常见问题及优化
- 虚假唤醒:在某些操作系统中,等待在条件变量上的线程可能会在没有被
notify
的情况下被唤醒,这就是虚假唤醒。 - 避免虚假唤醒:在等待条件变量时,使用
while
循环来检查条件,而不是if
语句。这样即使发生虚假唤醒,线程也会再次检查条件,只有条件真正满足时才会继续执行。
生产者 - 消费者模型代码示例
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> q;
const int max_size = 10;
void producer(int id) {
for (int i = 0; i < 20; ++i) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return q.size() < max_size; });
q.push(i);
std::cout << "Producer " << id << " produced: " << i << std::endl;
lock.unlock();
cv.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void consumer(int id) {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return!q.empty(); });
int value = q.front();
q.pop();
std::cout << "Consumer " << id << " consumed: " << value << std::endl;
lock.unlock();
cv.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
}
int main() {
std::thread p1(producer, 1);
std::thread p2(producer, 2);
std::thread c1(consumer, 1);
std::thread c2(consumer, 2);
p1.join();
p2.join();
c1.join();
c2.join();
return 0;
}
在上述代码中:
producer
函数模拟生产者,不断生成数据并放入队列q
中,当队列满时等待条件变量。consumer
函数模拟消费者,不断从队列q
中取出数据,当队列为空时等待条件变量。cv.wait
函数中的while
循环条件检查避免了虚假唤醒的影响。