面试题答案
一键面试C++ 代码实现生产者 - 消费者模型
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
std::queue<int> sharedQueue;
std::mutex queueMutex;
std::condition_variable cv;
const int queueSize = 5;
// 生产者线程函数
void producer(int id) {
for (int i = 0; i < 10; ++i) {
std::unique_lock<std::mutex> lock(queueMutex);
cv.wait(lock, [] { return sharedQueue.size() < queueSize; });
sharedQueue.push(id * 10 + i);
std::cout << "Producer " << id << " pushed " << id * 10 + 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(queueMutex);
cv.wait(lock, [] { return!sharedQueue.empty(); });
int data = sharedQueue.front();
sharedQueue.pop();
std::cout << "Consumer " << id << " popped " << data << std::endl;
lock.unlock();
cv.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
你可以通过以下方式调用:
int main() {
std::thread producers[3];
std::thread consumers[2];
for (int i = 0; i < 3; ++i) {
producers[i] = std::thread(producer, i);
}
for (int i = 0; i < 2; ++i) {
consumers[i] = std::thread(consumer, i);
}
for (auto& producer : producers) {
producer.join();
}
for (auto& consumer : consumers) {
consumer.join();
}
return 0;
}
std::condition_variable
使用场景
- 线程间同步:当一个线程需要等待某个条件满足才能继续执行时,例如消费者线程等待队列中有数据,生产者线程等待队列有空闲空间。
- 资源管理:在多线程环境下,控制对共享资源的访问。当资源达到某种状态(如满或空)时,通知相关线程进行相应操作。
可能遇到的死锁问题及避免方法
- 死锁问题:
- 缺少通知:如果生产者或消费者线程忘记调用
cv.notify_one()
或cv.notify_all()
,那么等待的线程将永远阻塞,导致死锁。 - 锁的持有问题:如果在调用
cv.wait
时没有正确释放锁(例如手动提前解锁),或者在cv.wait
返回后没有重新获取锁,可能导致数据竞争和死锁。例如,消费者线程在cv.wait
后,没有重新获取锁就访问共享队列,可能导致其他线程无法修改队列状态,造成死锁。
- 缺少通知:如果生产者或消费者线程忘记调用
- 避免方法:
- 确保通知:在条件满足时,务必调用
cv.notify_one()
或cv.notify_all()
。例如,生产者在往队列中添加数据后,调用cv.notify_one()
通知消费者;消费者在从队列中取出数据后,调用cv.notify_one()
通知生产者。 - 正确使用锁:使用
std::unique_lock
来管理锁,并将其作为参数传递给cv.wait
。std::unique_lock
会在cv.wait
时自动释放锁,并在cv.wait
返回时重新获取锁,避免手动管理锁带来的错误。例如在生产者和消费者函数中,使用std::unique_lock<std::mutex> lock(queueMutex);
来管理锁,并将lock
传递给cv.wait
。
- 确保通知:在条件满足时,务必调用