pthread_cond_broadcast
是 POSIX 线程(Pthreads)库中用于条件变量(Condition Variable)操作的函数,定义在 <pthread.h>
头文件中。它的核心作用是唤醒所有等待在某个条件变量上的线程,通常用于多线程同步的场景。
核心概念
-
条件变量(Condition Variable)
条件变量是线程同步的一种机制,允许线程在某个条件不满足时挂起(等待),并在条件可能满足时被唤醒。它必须与**互斥锁(Mutex)**配合使用。 -
pthread_cond_broadcast
的功能- 唤醒所有正在等待该条件变量的线程。
- 与
pthread_cond_signal
(仅唤醒一个线程)不同,broadcast
会唤醒所有线程,适用于需要多个线程同时响应某个条件变化的场景。
-
典型使用模式
// 线程等待条件 pthread_mutex_lock(&mutex); while (condition_is_false) {pthread_cond_wait(&cond, &mutex); } // 处理条件满足后的操作 pthread_mutex_unlock(&mutex);// 另一线程触发条件 pthread_mutex_lock(&mutex); // 修改条件 condition_is_true = 1; pthread_cond_broadcast(&cond); // 唤醒所有等待线程 pthread_mutex_unlock(&mutex);
使用案例:任务队列的线程池
以下示例演示了一个简单的线程池,多个工作线程从任务队列中获取任务。当有新任务加入时,主线程通过 pthread_cond_broadcast
唤醒所有工作线程。
代码实现
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>#define MAX_TASKS 10
#define NUM_WORKERS 3// 任务结构体
typedef struct {int id;
} Task;// 共享任务队列
Task task_queue[MAX_TASKS];
int task_count = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;// 工作线程函数
void* worker(void* arg) {int worker_id = *(int*)arg;while (1) {pthread_mutex_lock(&mutex);// 等待任务队列非空while (task_count == 0) {printf("Worker %d: waiting...\n", worker_id);pthread_cond_wait(&cond, &mutex);}// 取出任务Task task = task_queue[--task_count];printf("Worker %d: processing task %d\n", worker_id, task.id);pthread_mutex_unlock(&mutex);// 模拟任务处理sleep(1);}return NULL;
}int main() {pthread_t workers[NUM_WORKERS];int worker_ids[NUM_WORKERS];// 创建工作线程for (int i = 0; i < NUM_WORKERS; i++) {worker_ids[i] = i + 1;pthread_create(&workers[i], NULL, worker, &worker_ids[i]);}// 主线程添加任务for (int i = 0; i < 5; i++) {pthread_mutex_lock(&mutex);if (task_count < MAX_TASKS) {Task new_task = { .id = i + 1 };task_queue[task_count++] = new_task;printf("Main: added task %d\n", new_task.id);// 唤醒所有工作线程pthread_cond_broadcast(&cond);}pthread_mutex_unlock(&mutex);sleep(1);}// 等待工作线程结束(此处简化,实际需添加退出逻辑)for (int i = 0; i < NUM_WORKERS; i++) {pthread_join(workers[i], NULL);}pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond);return 0;
}
代码解析
-
共享资源保护
task_queue
和task_count
是共享资源,通过pthread_mutex_t mutex
保护。- 线程在访问任务队列前必须获取锁。
-
条件变量的等待与唤醒
- 工作线程调用
pthread_cond_wait
挂起,直到任务队列非空。 - 主线程添加任务后,调用
pthread_cond_broadcast
唤醒所有等待的线程。
- 工作线程调用
-
broadcast
的必要性
如果使用pthread_cond_signal
(仅唤醒一个线程),可能导致任务处理效率低下。例如:- 添加多个任务时,需要多次调用
signal
。 - 使用
broadcast
可一次性唤醒所有线程,让它们竞争处理任务,提高并发性。
- 添加多个任务时,需要多次调用
关键注意事项
-
虚假唤醒(Spurious Wakeup)
线程可能在没有收到信号的情况下被唤醒,因此必须用while
循环检查条件,而非if
语句:while (task_count == 0) {pthread_cond_wait(&cond, &mutex); }
-
锁的释放与重新获取
pthread_cond_wait
会自动释放锁并挂起线程。- 被唤醒后,线程会重新获取锁,继续执行后续代码。
-
性能权衡
broadcast
会唤醒所有线程,可能导致资源竞争。如果只需唤醒一个线程,优先使用pthread_cond_signal
。
典型应用场景
-
资源池管理
当资源(如数据库连接)可用时,唤醒所有等待线程竞争资源。 -
事件广播
例如,多个线程需要同时响应某个全局状态的变化(如配置文件更新)。 -
生产者-消费者模型
生产者一次性添加多个任务时,唤醒所有消费者线程。
常见问题
-
何时用
broadcast
,何时用signal
?- 如果条件的变化可能让多个线程同时满足执行条件,用
broadcast
(如任务队列新增多个任务)。 - 如果条件的变化只允许一个线程继续执行,用
signal
(如单资源可用)。
- 如果条件的变化可能让多个线程同时满足执行条件,用
-
为什么用
while
检查条件?
防止虚假唤醒或条件被其他线程修改。 -
如何终止等待的线程?
通常设置一个退出标志,并在终止前调用broadcast
:// 设置退出标志 pthread_mutex_lock(&mutex); exit_flag = 1; pthread_cond_broadcast(&cond); pthread_mutex_unlock(&mutex);
通过合理使用 pthread_cond_broadcast
,可以实现高效的多线程同步,尤其适用于需要批量唤醒线程的场景。