面试题答案
一键面试设计思路
- 初始化:初始化互斥锁和条件变量,为线程间同步做准备。
- 生产者线程:生产数据,获取互斥锁,将数据放入共享缓冲区,然后通知等待在条件变量上的消费者线程。
- 消费者线程:获取互斥锁,检查共享缓冲区是否有数据,若没有则等待在条件变量上,有数据则取出数据并处理。
- 清理:程序结束时,销毁互斥锁和条件变量。
核心代码片段
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_SIZE 10
int buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// 生产者线程函数
void *producer(void *arg) {
int item = 0;
while (1) {
pthread_mutex_lock(&mutex);
while ((in + 1) % BUFFER_SIZE == out) {
// 缓冲区满,等待
pthread_cond_wait(&cond, &mutex);
}
buffer[in] = item++;
printf("Produced: %d\n", buffer[in]);
in = (in + 1) % BUFFER_SIZE;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
// 消费者线程函数
void *consumer(void *arg) {
while (1) {
pthread_mutex_lock(&mutex);
while (in == out) {
// 缓冲区空,等待
pthread_cond_wait(&cond, &mutex);
}
int item = buffer[out];
printf("Consumed: %d\n", item);
out = (out + 1) % BUFFER_SIZE;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
在 main
函数中创建生产者和消费者线程:
int main() {
pthread_t producer_thread, consumer_thread;
pthread_create(&producer_thread, NULL, producer, NULL);
pthread_create(&consumer_thread, NULL, consumer, NULL);
pthread_join(producer_thread, NULL);
pthread_join(consumer_thread, NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
条件变量与互斥锁配合使用要点
- 先加锁:在调用
pthread_cond_wait
之前必须先获取互斥锁,因为pthread_cond_wait
函数会自动释放互斥锁并使线程等待,当被唤醒时又会重新获取该互斥锁。 - 检查条件:在等待条件变量时,应该使用循环检查条件,而不是单次检查,以防止虚假唤醒(spurious wakeup)。虚假唤醒是指线程可能在没有调用
pthread_cond_signal
或pthread_cond_broadcast
的情况下被唤醒。 - 信号发送:在条件满足时,调用
pthread_cond_signal
或pthread_cond_broadcast
通知等待在条件变量上的线程。pthread_cond_signal
通常用于唤醒单个等待线程,pthread_cond_broadcast
用于唤醒所有等待线程。在调用这些函数前,也需要持有互斥锁。 - 锁的范围:确保互斥锁保护的是与条件变量相关的共享资源,且在使用条件变量的整个过程中,对共享资源的访问都在互斥锁的保护下。
- 清理:程序结束时,务必正确销毁互斥锁和条件变量,避免资源泄漏。