面试题答案
一键面试条件变量的原理
条件变量是一种同步机制,用于线程间的协调。它通常与互斥锁配合使用。
- 基本概念:
- 条件变量允许线程等待某个条件为真。例如,在生产者 - 消费者模型中,消费者线程可以等待共享缓冲区不为空的条件,生产者线程可以等待共享缓冲区不满的条件。
- 当一个线程发现条件不满足时,它可以通过条件变量进入睡眠状态,并自动释放相关的互斥锁(以避免死锁)。
- 当另一个线程改变了条件(例如生产者向共享缓冲区放入数据使缓冲区不为空),它可以唤醒等待在条件变量上的线程。被唤醒的线程会重新获取互斥锁,然后检查条件是否满足。
关键代码实现
#include <stdio.h>
#include <stdlib.h>
#include <pthread.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 = 1;
while (1) {
pthread_mutex_lock(&mutex);
while (count == BUFFER_SIZE) {
pthread_cond_wait(¬_full, &mutex);
}
buffer[in] = item;
printf("Produced: %d\n", item);
in = (in + 1) % BUFFER_SIZE;
count++;
pthread_cond_signal(¬_empty);
pthread_mutex_unlock(&mutex);
item++;
}
return NULL;
}
// 消费者线程函数
void* consumer(void* arg) {
while (1) {
pthread_mutex_lock(&mutex);
while (count == 0) {
pthread_cond_wait(¬_empty, &mutex);
}
int item = buffer[out];
printf("Consumed: %d\n", item);
out = (out + 1) % BUFFER_SIZE;
count--;
pthread_cond_signal(¬_full);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
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(¬_full);
pthread_cond_destroy(¬_empty);
return 0;
}
避免条件变量使用过程中的常见错误
- 忘记加锁:
- 条件变量的操作(
pthread_cond_wait
、pthread_cond_signal
等)必须在持有相关互斥锁的情况下进行。例如,在上述代码中,pthread_cond_wait
和pthread_cond_signal
都在pthread_mutex_lock
和pthread_mutex_unlock
之间。
- 条件变量的操作(
- 使用
while
循环检查条件:- 不能使用
if
语句检查条件。因为pthread_cond_wait
被唤醒后,可能是虚假唤醒(即使没有其他线程调用pthread_cond_signal
或pthread_cond_broadcast
也可能被唤醒)。所以应该使用while
循环来重新检查条件,如代码中的while (count == BUFFER_SIZE)
和while (count == 0)
。
- 不能使用
- 正确使用信号:
- 选择合适的信号方式。
pthread_cond_signal
唤醒一个等待的线程,pthread_cond_broadcast
唤醒所有等待的线程。在生产者 - 消费者模型中,pthread_cond_signal
通常就足够了,因为每次生产或消费操作只会影响一个等待的线程。但如果有多个不同类型的等待线程,可能需要使用pthread_cond_broadcast
。
- 选择合适的信号方式。
- 资源清理:
- 在程序结束时,要正确销毁条件变量和互斥锁。如上述代码中的
pthread_mutex_destroy
、pthread_cond_destroy
操作,避免资源泄漏。
- 在程序结束时,要正确销毁条件变量和互斥锁。如上述代码中的