面试题答案
一键面试使用条件变量和互斥锁实现生产者 - 消费者模型
-
初始化:
- 创建一个互斥锁(Mutex)用于保护共享缓冲区的访问。
- 创建两个条件变量(Condition Variable),一个用于生产者等待缓冲区有空闲空间(
not_full
),另一个用于消费者等待缓冲区有数据(not_empty
)。 - 定义共享缓冲区及其大小,以及用于记录缓冲区数据数量的变量。
-
生产者代码:
// 假设使用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(¬_full, &mutex);
}
buffer[in] = item;
in = (in + 1) % BUFFER_SIZE;
count++;
printf("Produced: %d\n", item);
pthread_cond_signal(¬_empty);
pthread_mutex_unlock(&mutex);
return NULL;
}
- 消费者代码:
void *consumer(void *arg) {
pthread_mutex_lock(&mutex);
while (count == 0) {
pthread_cond_wait(¬_empty, &mutex);
}
int item = buffer[out];
out = (out + 1) % BUFFER_SIZE;
count--;
printf("Consumed: %d\n", item);
pthread_cond_signal(¬_full);
pthread_mutex_unlock(&mutex);
return NULL;
}
多生产者 - 多消费者场景下可能出现的问题及解决方案
- 虚假唤醒问题:
- 问题:条件变量可能在没有相应条件满足时被唤醒(虚假唤醒),导致程序逻辑错误。
- 解决方案:在等待条件变量时使用循环检测条件,而不是只检测一次。如上述代码中生产者和消费者等待时都使用
while
循环检测缓冲区状态。
- 死锁问题:
- 问题:如果多个线程获取锁和等待条件变量的顺序不一致,可能会导致死锁。例如,一个线程先获取锁然后等待条件变量,而另一个线程等待条件变量被唤醒后尝试获取锁,但此时第一个线程还未释放锁。
- 解决方案:确保所有线程以一致的顺序获取锁和等待条件变量。同时,避免在持有锁的情况下进行长时间的计算或阻塞操作,以减少死锁风险。
- 饥饿问题:
- 问题:某些生产者或消费者线程可能长时间得不到执行机会,导致饥饿。例如,高优先级线程持续获取锁,使得低优先级线程长时间无法访问共享缓冲区。
- 解决方案:可以采用公平调度算法,如使用条件变量的公平属性(在一些系统中支持),或者在应用层实现轮询机制,确保每个线程都有机会执行。例如,可以记录每个线程等待的时间,当某个线程等待时间过长时,优先唤醒它。