# 条件变量 (Condition Variable) ## 条件变量概念 条件变量是线程间同步的等待/通知机制,用于在线程之间传递事件通知。它必须与互斥锁配合使用。 **核心思想**:一个线程等待某个条件成立,另一个线程在条件成立时发出通知。 --- ## 基本操作 ### 初始化与销毁 ```c #include // 动态初始化 int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); // 静态初始化 pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 销毁 int pthread_cond_destroy(pthread_cond_t *cond); ``` ### 等待与通知 ```c // 等待条件变量(原子释放锁+等待通知) int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); // 通知一个等待线程 int pthread_cond_signal(pthread_cond_t *cond); // 通知所有等待线程 int pthread_cond_broadcast(pthread_cond_t *cond); ``` ### 带超时的等待 ```c int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); ``` --- ## pthread_cond_wait 详解 `pthread_cond_wait` 的执行过程: 1. 原子地释放互斥锁 2. 线程进入等待状态 3. 被通知后,重新获取互斥锁 4. 返回调用者 **关键点**: - 必须在持有互斥锁时调用 - 返回时会重新获取互斥锁 - 可能出现虚假唤醒,需要循环检查条件 **错误使用示例**: ```c // 错误:没有持有互斥锁 pthread_cond_wait(&cond, &mutex); // 未定义行为 // 错误:没有循环检查条件 pthread_mutex_lock(&mutex); if (!condition) { pthread_cond_wait(&cond, &mutex); } // 处理条件 // 可能虚假唤醒导致错误 pthread_mutex_unlock(&mutex); ``` **正确使用模式**: ```c pthread_mutex_lock(&mutex); while (!condition) { pthread_cond_wait(&cond, &mutex); } // 处理条件 pthread_mutex_unlock(&mutex); ``` --- ## 虚假唤醒 (Spurious Wakeup) ### 什么是虚假唤醒 条件变量可能在没有 `signal` 或 `broadcast` 的情况下唤醒线程,这是 POSIX 标准允许的行为。 ### 处理方法 使用 `while` 循环检查条件,而不是 `if`。 ```c // 错误:可能虚假唤醒 if (count > 0) { count--; } // 正确:循环检查 while (count <= 0) { pthread_cond_wait(&cond, &mutex); } count--; ``` --- ## 经典问题:生产者-消费者完整实现 ### 有界缓冲区实现 ```c #include #include #include #include #define BUFFER_SIZE 5 #define NUM_ITEMS 20 typedef struct { int buffer[BUFFER_SIZE]; int in; // 生产者写入位置 int out; // 消费者读取位置 int count; // 当前缓冲区中的物品数量 pthread_mutex_t mutex; pthread_cond_t not_full; // 缓冲区不满条件 pthread_cond_t not_empty; // 缓冲区不空条件 } BoundedBuffer; void buffer_init(BoundedBuffer* buf) { buf->in = 0; buf->out = 0; buf->count = 0; pthread_mutex_init(&buf->mutex, NULL); pthread_cond_init(&buf->not_full, NULL); pthread_cond_init(&buf->not_empty, NULL); } void buffer_destroy(BoundedBuffer* buf) { pthread_mutex_destroy(&buf->mutex); pthread_cond_destroy(&buf->not_full); pthread_cond_destroy(&buf->not_empty); } void buffer_produce(BoundedBuffer* buf, int item) { pthread_mutex_lock(&buf->mutex); // 等待缓冲区不满 while (buf->count >= BUFFER_SIZE) { printf("Producer: buffer full, waiting...\n"); pthread_cond_wait(&buf->not_full, &buf->mutex); } // 生产物品 buf->buffer[buf->in] = item; buf->in = (buf->in + 1) % BUFFER_SIZE; buf->count++; printf("Producer: produced %d, count: %d\n", item, buf->count); // 通知消费者有数据 pthread_cond_signal(&buf->not_empty); pthread_mutex_unlock(&buf->mutex); } int buffer_consume(BoundedBuffer* buf) { pthread_mutex_lock(&buf->mutex); // 等待缓冲区不空 while (buf->count <= 0) { printf("Consumer: buffer empty, waiting...\n"); pthread_cond_wait(&buf->not_empty, &buf->mutex); } // 消费物品 int item = buf->buffer[buf->out]; buf->out = (buf->out + 1) % BUFFER_SIZE; buf->count--; printf("Consumer: consumed %d, count: %d\n", item, buf->count); // 通知生产者有空间 pthread_cond_signal(&buf->not_full); pthread_mutex_unlock(&buf->mutex); return item; } void* producer(void* arg) { BoundedBuffer* buf = (BoundedBuffer*)arg; for (int i = 0; i < NUM_ITEMS; i++) { buffer_produce(buf, i); usleep(100000); // 100ms } return NULL; } void* consumer(void* arg) { BoundedBuffer* buf = (BoundedBuffer*)arg; for (int i = 0; i < NUM_ITEMS; i++) { int item = buffer_consume(buf); usleep(150000); // 150ms } return NULL; } int main() { BoundedBuffer buf; buffer_init(&buf); pthread_t prod_thread, cons_thread; pthread_create(&prod_thread, NULL, producer, &buf); pthread_create(&cons_thread, NULL, consumer, &buf); pthread_join(prod_thread, NULL); pthread_join(cons_thread, NULL); buffer_destroy(&buf); return 0; } ``` **编译命令**: ```bash gcc -o bounded_buffer bounded_buffer.c -pthread ``` --- ## 嵌入式场景:事件驱动架构 ### 事件通知系统 ```c #include #include #include #include #define NUM_EVENTS 10 typedef enum { EVENT_TYPE_A, EVENT_TYPE_B, EVENT_TYPE_C, EVENT_TYPE_EXIT } EventType; typedef struct { EventType type; int data; } Event; typedef struct { Event events[NUM_EVENTS]; int head; int tail; int count; pthread_mutex_t mutex; pthread_cond_t event_available; int running; } EventQueue; void queue_init(EventQueue* queue) { queue->head = 0; queue->tail = 0; queue->count = 0; queue->running = 1; pthread_mutex_init(&queue->mutex, NULL); pthread_cond_init(&queue->event_available, NULL); } void queue_destroy(EventQueue* queue) { queue->running = 0; pthread_cond_broadcast(&queue->event_available); pthread_mutex_destroy(&queue->mutex); pthread_cond_destroy(&queue->event_available); } int queue_push(EventQueue* queue, Event* event) { pthread_mutex_lock(&queue->mutex); if (queue->count >= NUM_EVENTS) { pthread_mutex_unlock(&queue->mutex); return -1; } queue->events[queue->tail] = *event; queue->tail = (queue->tail + 1) % NUM_EVENTS; queue->count++; pthread_cond_signal(&queue->event_available); pthread_mutex_unlock(&queue->mutex); return 0; } int queue_pop(EventQueue* queue, Event* event) { pthread_mutex_lock(&queue->mutex); while (queue->count == 0 && queue->running) { pthread_cond_wait(&queue->event_available, &queue->mutex); } if (!queue->running && queue->count == 0) { pthread_mutex_unlock(&queue->mutex); return -1; } *event = queue->events[queue->head]; queue->head = (queue->head + 1) % NUM_EVENTS; queue->count--; pthread_mutex_unlock(&queue->mutex); return 0; } void* event_handler(void* arg) { EventQueue* queue = (EventQueue*)arg; Event event; while (queue_pop(queue, &event) == 0) { switch (event.type) { case EVENT_TYPE_A: printf("Handler: Event A with data %d\n", event.data); break; case EVENT_TYPE_B: printf("Handler: Event B with data %d\n", event.data); break; case EVENT_TYPE_C: printf("Handler: Event C with data %d\n", event.data); break; case EVENT_TYPE_EXIT: printf("Handler: Exit event received\n"); return NULL; default: printf("Handler: Unknown event type\n"); } } return NULL; } void* event_generator(void* arg) { EventQueue* queue = (EventQueue*)arg; Event event; for (int i = 0; i < 5; i++) { event.type = EVENT_TYPE_A; event.data = i; queue_push(queue, &event); usleep(100000); event.type = EVENT_TYPE_B; event.data = i * 10; queue_push(queue, &event); usleep(100000); } // 发送退出事件 event.type = EVENT_TYPE_EXIT; queue_push(queue, &event); return NULL; } int main() { EventQueue queue; queue_init(&queue); pthread_t handler_thread, generator_thread; pthread_create(&handler_thread, NULL, event_handler, &queue); pthread_create(&generator_thread, NULL, event_generator, &queue); pthread_join(generator_thread, NULL); pthread_join(handler_thread, NULL); queue_destroy(&queue); return 0; } ``` **编译命令**: ```bash gcc -o event_queue event_queue.c -pthread ``` --- ## 注意事项 1. **必须配合互斥锁使用**:条件变量不能独立使用 2. **虚假唤醒**:始终使用 `while` 循环检查条件 3. **通知丢失**:`signal` 可能丢失,需要确保条件被检查 4. **销毁时机**:确保没有线程在等待时销毁条件变量 5. **性能考虑**:避免不必要的唤醒 --- ## 面试要点 ### Q1: 为什么条件变量需要与互斥锁配合使用? **A**: - 条件检查需要原子性 - 防止信号丢失 - 确保条件检查的正确性 ### Q2: `pthread_cond_signal` 和 `pthread_cond_broadcast` 的区别? **A**: - `signal`:唤醒一个等待线程 - `broadcast`:唤醒所有等待线程 - 选择取决于具体需求 ### Q3: 什么是虚假唤醒?如何处理? **A**: - 虚假唤醒:条件变量在没有通知的情况下唤醒线程 - 处理:使用 `while` 循环检查条件 ### Q4: 如何避免条件变量的通知丢失? **A**: 1. 先检查条件,再决定是否等待 2. 使用 `while` 循环确保条件被检查 3. 在修改共享变量后发送通知 ### Q5: 条件变量在嵌入式系统中的应用场景? **A**: 1. 事件驱动架构 2. 生产者-消费者模型 3. 线程池管理 4. 资源池管理 --- ## 相关链接 - [[10. 互斥锁]] - [[8. 线程基础]]