MST
星途 面试题库

面试题:并发与同步之条件变量的复杂应用

假设有一个生产者 - 消费者模型,生产者向共享缓冲区写入数据,消费者从共享缓冲区读取数据。共享缓冲区有固定大小,当缓冲区满时生产者需等待,当缓冲区空时消费者需等待。请详细说明如何使用条件变量和互斥锁来实现这个模型,并且阐述在多生产者 - 多消费者场景下可能出现的问题及解决方案。
46.1万 热度难度
操作系统并发与同步

知识考点

AI 面试

面试题答案

一键面试

使用条件变量和互斥锁实现生产者 - 消费者模型

  1. 初始化

    • 创建一个互斥锁(Mutex)用于保护共享缓冲区的访问。
    • 创建两个条件变量(Condition Variable),一个用于生产者等待缓冲区有空闲空间(not_full),另一个用于消费者等待缓冲区有数据(not_empty)。
    • 定义共享缓冲区及其大小,以及用于记录缓冲区数据数量的变量。
  2. 生产者代码

// 假设使用C语言
#include <pthread.h>
#include <stdio.h>

#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
int count = 0;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;

void *producer(void *arg) {
    int item = *((int *)arg);
    pthread_mutex_lock(&mutex);
    while (count == BUFFER_SIZE) {
        pthread_cond_wait(&not_full, &mutex);
    }
    buffer[in] = item;
    in = (in + 1) % BUFFER_SIZE;
    count++;
    printf("Produced: %d\n", item);
    pthread_cond_signal(&not_empty);
    pthread_mutex_unlock(&mutex);
    return NULL;
}
  1. 消费者代码
void *consumer(void *arg) {
    pthread_mutex_lock(&mutex);
    while (count == 0) {
        pthread_cond_wait(&not_empty, &mutex);
    }
    int item = buffer[out];
    out = (out + 1) % BUFFER_SIZE;
    count--;
    printf("Consumed: %d\n", item);
    pthread_cond_signal(&not_full);
    pthread_mutex_unlock(&mutex);
    return NULL;
}

多生产者 - 多消费者场景下可能出现的问题及解决方案

  1. 虚假唤醒问题
    • 问题:条件变量可能在没有相应条件满足时被唤醒(虚假唤醒),导致程序逻辑错误。
    • 解决方案:在等待条件变量时使用循环检测条件,而不是只检测一次。如上述代码中生产者和消费者等待时都使用while循环检测缓冲区状态。
  2. 死锁问题
    • 问题:如果多个线程获取锁和等待条件变量的顺序不一致,可能会导致死锁。例如,一个线程先获取锁然后等待条件变量,而另一个线程等待条件变量被唤醒后尝试获取锁,但此时第一个线程还未释放锁。
    • 解决方案:确保所有线程以一致的顺序获取锁和等待条件变量。同时,避免在持有锁的情况下进行长时间的计算或阻塞操作,以减少死锁风险。
  3. 饥饿问题
    • 问题:某些生产者或消费者线程可能长时间得不到执行机会,导致饥饿。例如,高优先级线程持续获取锁,使得低优先级线程长时间无法访问共享缓冲区。
    • 解决方案:可以采用公平调度算法,如使用条件变量的公平属性(在一些系统中支持),或者在应用层实现轮询机制,确保每个线程都有机会执行。例如,可以记录每个线程等待的时间,当某个线程等待时间过长时,优先唤醒它。