MST
星途 面试题库

面试题:并发与同步之条件变量基础作用

在操作系统的并发编程中,简单描述条件变量在同步机制里是如何配合互斥锁使用来实现线程间同步的,并举例说明一个可能用到这种配合的场景。
49.1万 热度难度
操作系统并发与同步

知识考点

AI 面试

面试题答案

一键面试

条件变量与互斥锁配合实现线程间同步的原理

  1. 互斥锁:用于保护共享资源,确保同一时间只有一个线程能够访问共享资源,避免数据竞争。线程在访问共享资源前需要获取互斥锁,访问结束后释放互斥锁。
  2. 条件变量:本身并不保护共享资源,它主要用于线程间的通信和同步。条件变量允许线程等待某个条件满足后再继续执行。线程通常在获取互斥锁后,检查条件是否满足,如果不满足,则通过条件变量进入等待状态,并同时释放之前获取的互斥锁,以便其他线程可以修改共享资源。当条件满足时,其他线程可以通过条件变量唤醒等待的线程,被唤醒的线程重新获取互斥锁后继续执行。

示例场景:生产者 - 消费者模型

  1. 场景描述:假设有一个生产者线程和多个消费者线程,生产者线程向一个共享缓冲区中生产数据,消费者线程从共享缓冲区中消费数据。共享缓冲区有一定的容量限制。
  2. 实现方式
    • 互斥锁:用于保护共享缓冲区,确保在同一时间只有一个线程(生产者或消费者)能够访问缓冲区,防止数据竞争。
    • 条件变量:有两个条件变量,一个用于通知消费者缓冲区有新数据(not_empty),另一个用于通知生产者缓冲区有空闲空间(not_full)。
    • 生产者线程
      • 首先获取互斥锁。
      • 检查缓冲区是否已满,如果已满,则通过not_full条件变量等待,同时释放互斥锁。
      • 当被唤醒后,重新获取互斥锁,向缓冲区中写入数据。
      • 写入完成后,释放互斥锁,并通过not_empty条件变量通知消费者线程缓冲区有新数据。
    • 消费者线程
      • 首先获取互斥锁。
      • 检查缓冲区是否为空,如果为空,则通过not_empty条件变量等待,同时释放互斥锁。
      • 当被唤醒后,重新获取互斥锁,从缓冲区中读取数据。
      • 读取完成后,释放互斥锁,并通过not_full条件变量通知生产者线程缓冲区有空闲空间。

以下是一个简化的伪代码示例(以C++ 为例,使用std::mutexstd::condition_variable):

#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable not_empty;
std::condition_variable not_full;
std::queue<int> buffer;
const int buffer_size = 5;

void producer(int id) {
    for (int i = 0; i < 10; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        while (buffer.size() == buffer_size) {
            not_full.wait(lock);
        }
        buffer.push(i);
        std::cout << "Producer " << id << " produced " << i << std::endl;
        lock.unlock();
        not_empty.notify_one();
    }
}

void consumer(int id) {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        while (buffer.empty()) {
            not_empty.wait(lock);
        }
        int data = buffer.front();
        buffer.pop();
        std::cout << "Consumer " << id << " consumed " << data << std::endl;
        lock.unlock();
        not_full.notify_one();
        if (data == 9) break;
    }
}

int main() {
    std::thread producer_thread1(producer, 1);
    std::thread producer_thread2(producer, 2);
    std::thread consumer_thread1(consumer, 1);
    std::thread consumer_thread2(consumer, 2);

    producer_thread1.join();
    producer_thread2.join();
    consumer_thread1.join();
    consumer_thread2.join();

    return 0;
}

在上述代码中,生产者和消费者线程通过互斥锁和条件变量实现了同步,确保共享缓冲区的正确使用。