MST

星途 面试题库

面试题:C++条件变量在多线程同步中的应用及优化

在C++多线程环境下,条件变量(condition_variable)常被用于线程间的复杂同步。请说明条件变量和互斥锁协同工作的机制。假设你正在开发一个生产者 - 消费者模型,使用条件变量来实现线程间的高效同步,描述可能会遇到的常见问题,例如虚假唤醒,并说明如何优化代码以避免这些问题。请用代码示例展示生产者 - 消费者模型的实现。
45.7万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

条件变量和互斥锁协同工作机制

  1. 互斥锁(mutex):用于保护共享资源,确保同一时间只有一个线程可以访问共享资源,避免数据竞争。
  2. 条件变量(condition_variable):用于线程间的同步,当某个条件满足时,通知等待在条件变量上的线程。
  3. 协同工作过程
    • 线程首先获取互斥锁,进入临界区,检查共享资源的状态(条件)。
    • 如果条件不满足,线程调用条件变量的等待函数(如cv.wait),这个过程中,线程会自动释放持有的互斥锁,并进入等待状态。
    • 当另一个线程修改了共享资源的状态,使其满足条件时,该线程获取互斥锁,然后调用条件变量的通知函数(如cv.notify_onecv.notify_all),唤醒一个或所有等待在条件变量上的线程。
    • 被唤醒的线程重新获取互斥锁,重新进入临界区,再次检查条件是否满足(因为可能存在虚假唤醒)。

生产者 - 消费者模型中常见问题及优化

  1. 虚假唤醒:在某些操作系统中,等待在条件变量上的线程可能会在没有被notify的情况下被唤醒,这就是虚假唤醒。
  2. 避免虚假唤醒:在等待条件变量时,使用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循环条件检查避免了虚假唤醒的影响。