pthread_cond_broadcast使用示例

本文探讨了在使用pthread_cond_t时遇到的问题,特别是在使用pthread_cond_broadcast函数唤醒多个条件变量时,发现仅能唤醒其中一个的情况。文章通过代码示例详细解释了如何正确使用互斥量来避免条件丢失,并讨论了为何使用两个互斥量会导致问题。

今天在使用pthread_cond_t时,发现pthread_cond_t使用pthread_cond_broadcast函数唤醒多个条件变量时,使用两个互斥量分别控制时,只能唤醒其中一个变量,最后通过测试发现只能使用一个互斥量去控制。
个人理解:
thread1中使用pthread_mutex_lock(&mutex1);加互斥所,然后在pthread_cond_wait(&cond, &mutex1);这样可以保证这期间没有pthread_cond_broadcast或者pthread_cond_signal操作,从而保证条件不会丢失,当pthread_cond_wait(&cond, &mutex1);
完成操作之后,会自动释放互斥量。
然后thread2中的互斥量也紧接着根据上述方式加入等待对列,当调用pthread_cond_broadcast时会根据加入等待队列中的先后顺序依次唤醒他们。当是还是不明白为什么使用两个互斥量就只能唤醒其中一个呢?有清楚的朋友可以留言。

#include <stdio.h>
#include <pthread.h>

static pthread_cond_t cond;
static pthread_mutex_t mutex1;
static pthread_mutex_t mutex2;
void *thread1_entry(void *arg)
{
        while(1)
        {
                pthread_mutex_lock(&mutex1);
                printf("cond wait cond1\n");
                pthread_cond_wait(&cond, &mutex1);
                printf("recv cond1\n");
                pthread_mutex_unlock(&mutex1);

        }
}

void *thread2_entry(void *arg)
{
        while(1)
        {
                pthread_mutex_lock(&mutex1);
                printf("cond wait cond2\n");
                pthread_cond_wait(&cond, &mutex1);
                printf("recv cond2\n");
                pthread_mutex_unlock(&mutex1);
        }

}

void *thread3_entry(void *arg)
{
        int ret;
        while(1)
        {
                pthread_mutex_lock(&mutex1);
                pthread_mutex_lock(&mutex2);
                ret = pthread_cond_broadcast(&cond);
                if(ret < 0)
                {
                        printf("pthread_cond_broadcast error\n");
                }
                pthread_mutex_unlock(&mutex2);
                pthread_mutex_unlock(&mutex1);
                sleep(2);
        }
}
int main(void)
{
        int ret =0;
        pthread_t tid1, tid2, tid3;
        ret = pthread_cond_init(&cond, NULL);
        if(ret < 0)
        {
                printf("pthread_cond_init error\n");
        }
        ret = pthread_mutex_init(&mutex1, NULL);
        if(ret < 0)
        {
                printf("pthread_mutex_init error\n");
        }

        ret = pthread_mutex_init(&mutex2,NULL);
        if(ret < 0)
        {
                printf("pthread_mutex_init error\n");
        }

        ret=  pthread_create(&tid1, NULL, thread1_entry, NULL);
        if(ret < 0)
        {
                printf("pthread_create thread1 error\n");
        }

        ret = pthread_create(&tid2, NULL, thread2_entry, NULL);
        if(ret < 0)
        {
                printf("pthread_create thread2 error\n");
        }
        sleep(2);
        ret = pthread_create(&tid3, NULL, thread3_entry, NULL);
        if(ret < 0)
        {
                printf("pthread_create thread3 error\n");
        }
#if 0
        pthread_mutex_lock(&mutex1);
        pthread_mutex_lock(&mutex2);
        ret = pthread_cond_broadcast(&cond);
        if(ret < 0)
        {
                printf("pthread_cond_broadcast error\n");
        }
        pthread_mutex_unlock(&mutex1);
        pthread_mutex_unlock(&mutex2);
#endif
        while(1)
        {
                sleep(10000);
        }
        return 0;
}
<think>我们正在处理一个关于线程条件广播操作的问题。用户询问了`pthread_cond_broadcast`的使用方法和注意事项。根据提供的引用内容,我们可以总结如下:引用[1]:`pthread_cond_broadcast`函数用于释放阻塞在条件上的所有线程。函数原型为:`int pthread_cond_broadcast(pthread_cond_t*cv);`成功返回0,否则返回错误码。引用[2]和[3]强调了使用条件时的一些重要注意事项:-当使用`pthread_cond_broadcast`唤醒所有线程时,通常我们只需要一部分线程工作,因此其他线程应该继续等待。因此,推荐使用while循环来检查条件,而不是if语句。-即使使用`pthread_cond_signal`(它通常唤醒一个线程),也可能在多处理器上唤醒多个线程,因此使用while循环是更安全的做法。-条件总是与互斥锁一起使用,当线程调用`pthread_cond_wait`时,它会自动释放互斥锁,并在返回前重新获取互斥锁。因此,使用`pthread_cond_broadcast`的典型场景是:当某个条件变为真,并且我们希望所有等待该条件线程都被唤醒时。例如,在资源可用时通知所有等待的线程使用步骤:1.线程在等待条件时,使用while循环(而不是if)来检查条件,这样可以避免虚假唤醒和不需要的唤醒。2.在改变条件之前,需要先锁定与条件关联的互斥锁。3.改变条件后,调用`pthread_cond_broadcast`(或`pthread_cond_signal`)来通知等待的线程。4.最后,释放互斥锁。注意事项:-使用while循环检查条件,防止虚假唤醒。-确保在调用`pthread_cond_wait`之前,线程已经锁定了互斥锁。- `pthread_cond_broadcast`会唤醒所有等待该条件线程,因此要确保广播是必要的(因为唤醒所有线程可能带来性能开销)。-在广播之后,被唤醒的线程会竞争互斥锁,只有一个线程能获得锁,其他线程会阻塞在互斥锁上。当获得锁的线程解锁后,其他线程才能依次获得锁并检查条件。下面是一个简单的示例代码,展示如何使用条件和广播:</think>### `pthread_cond_broadcast` 使用方法及注意事项 #### 一、基本功能 `pthread_cond_broadcast` 是 POSIX 线程库中用于**唤醒所有阻塞在条件上的线程**的函数。其原型为: ```c #include <pthread.h> int pthread_cond_broadcast(pthread_cond_t *cond); ``` - **参数**:`cond` 指向目标条件的指针 - **返回值**:成功返回 `0`,失败返回错误码(如 `EINVAL`) #### 二、典型使用场景 1. **资源可用性通知** 当多个线程等待共享资源(如线程池任务队列、缓冲区空间)变为可用时,使用广播唤醒所有等待线程竞争资源。 2. **状态全局变更** 当系统状态发生全局变化(如配置文件重载、服务终止信号)需要所有线程响应时。 3. **多消费者模型** 在生产者-消费者模型中,生产者添加多个资源后,可广播唤醒所有消费者线程[^3]。 #### 三、标准使用流程 ```c pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; bool condition = false; // 等待方线程 pthread_mutex_lock(&mutex); while (!condition) { // 必须用 while 而非 if pthread_cond_wait(&cond, &mutex); } // 执行任务... pthread_mutex_unlock(&mutex); // 广播方线程 pthread_mutex_lock(&mutex); condition = true; // 修改条件 pthread_cond_broadcast(&cond); // 唤醒所有等待者 pthread_mutex_unlock(&mutex); ``` #### 四、关键注意事项 1. **必须配合互斥锁使用** - 修改条件前必须锁住关联的互斥锁 - `pthread_cond_wait` 会在阻塞时**自动释放互斥锁**,返回时**重新获取锁**[^3] 2. **强制使用 while 循环检查条件** - 虚假唤醒(Spurious Wakeup):即使未广播,线程也可能被唤醒 - 过早唤醒:广播后线程可能被调度延迟,条件已失效 - 竞争条件:多个线程同时唤醒时资源可能被抢先消耗[^2][^3] 3. **性能考** - 广播会唤醒 **所有等待线程**,可能引发"惊群效应"(Thundering Herd) - 若只需唤醒少线程,优先使用 `pthread_cond_signal` - 示例线程池中任务数远小于等待线程数时[^2] 4. **条件设计原则** - 条件谓词应是**稳定状态**(如 `task_count > 0`) - 避免使用易变状态(如 `remaining_threads == 0`) #### 五、与 `pthread_cond_signal` 对比 | 特性 | `pthread_cond_broadcast` | `pthread_cond_signal` | |--------------------|--------------------------------|-------------------------| | 唤醒线程数 | 所有等待线程 | 至少一个(可能多个) | | 适用场景 | 全局状态变更/多资源可用 | 单资源可用/增更新 | | 系统开销 | 高(所有线程竞争锁) | 较低 | | 惊群效应风险 | 必然发生 | 可能发生(多处理器)[^3] | #### 六、错误处理 ```c int ret = pthread_cond_broadcast(&cond); if (ret != 0) { // 处理错误:EINVAL(无效条件)或 ENOMEM } ``` > **最佳实践**:在满足需求的前提下,优先使用 `pthread_cond_signal`;仅在需要**所有等待线程响应全局事件**时使用广播[^2][^3]。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值