pthread条件变量condition(配合mutex锁使用),经典,有图

本文介绍了如何使用pthread条件变量(conditional variable)和互斥锁(mutex)来实现线程间的同步。通过一个实例,详细解释了cond_wait与cond_signal函数的工作原理及正确使用方式。

转载自:http://blog.sina.com.cn/s/blog_967817f20101bsf0.html

为了便于理解,画了个图,一系列动作完成下来只需要一个mutex和一个conditional_variable。



wait函数包含了很多操作,在wait之前和之后必须要手动加锁/解锁mutex,保证同一时间只有一个人对条件变量cond进行使用。

先unlock或者先发送signal()都可以,Linux推荐吧signal()包在中间。

注意,即便以及发送了signal,却没有unlock,那么及时wait收到了信号,也无法返回,因为wait最后还需要锁住mutex,没抢到的话,只能在那继续阻塞。

 

pthread conditional_variable:

   

   cond_wait: (usage:)  lock-> cond_wait -> unlock

               (在调用wait时阻塞住线程,等待cond_signal的通知)

             What'sinside the function wait()? It unlocks the mutexand then blocks the thread.

            The purpose of lock is to prevent simultaneous requests ofwait().

            If cond_signal happens, it will stop blockingand lock themutex automatically.

   

   cond_signal:  (usage:) lock -> cond_signal ->unlock 

 

 

    here,lock and unlock means you have to write them explicitly in code byusing pthread_mutex_lock

<think>我们正在讨论pthread条件变量使用指南和工作原理。根据用户提供的引用和之前的问题,我们需要详细解释条件变量在多线程编程中的作用,以及如何正确使用它们。 引用[2]中展示了一个生产者-消费者模型的示例,其中使用了互斥来保护共享变量,但没有使用条件变量。这会导致消费者线程在无产品时仍然不断循环检查(忙等待),浪费CPU资源。而条件变量正是为了解决这类问题而设计的。 条件变量Condition Variable)是线程同步的一种机制,通常与互斥配合使用。它允许线程在某个条件不满足时进入睡眠状态,直到另一个线程通知条件可能已发生变化,从而唤醒等待的线程。 工作原理: 1. 线程在检查条件之前先获取互斥。 2. 检查条件,如果条件不满足,则使用条件变量等待(pthread_cond_wait)进入阻塞状态,同时该函数会释放互斥(以便其他线程可以修改条件)。 3. 当其他线程修改了条件并调用条件变量的信号(pthread_cond_signal)或广播(pthread_cond_broadcast)时,等待的线程被唤醒。注意:被唤醒的线程在返回前会重新获取互斥。 4. 线程被唤醒后,需要重新检查条件(因为可能有多个线程被唤醒,或者条件在唤醒后又被改变),如果条件满足则执行相应操作,否则继续等待。 使用条件变量的基本步骤: 1. 初始化条件变量和互斥pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 2. 等待条件的线程: pthread_mutex_lock(&mutex); while (条件不成立) { // 注意:必须用循环来等待条件,不能使用if(避免虚假唤醒) pthread_cond_wait(&cond, &mutex); // 等待期间会释放mutex,返回时重新获得 } // 执行条件满足后的操作 pthread_mutex_unlock(&mutex); 3. 改变条件并通知的线程: pthread_mutex_lock(&mutex); // 改变条件(例如,设置任务可用标志) // 然后发送信号 pthread_cond_signal(&cond); // 或者pthread_cond_broadcast(&cond); pthread_mutex_unlock(&mutex); 注意:条件变量本身并不存储条件,条件是由共享变量(例如任务队列长度、产品数量等)表示的。因此,在等待条件时,必须检查共享变量。 为什么使用while循环而不是if? 因为可能存在“虚假唤醒”(spurious wakeup),即线程在没有收到信号的情况下被唤醒。因此,被唤醒后必须重新检查条件。 修改引用[2]的生产者-消费者模型,加入条件变量: 修改后的消费者线程: static void *consumer_thread(void *arg) { for ( ; ; ) { pthread_mutex_lock(&mutex); while (g_avail == 0) { // 没有产品可消费时等待 pthread_cond_wait(&cond, &mutex); // 等待时释放mutex,唤醒后自动重新获得 } g_avail--; // 消费 pthread_mutex_unlock(&mutex); } return (void *)0; } 生产者线程(主线程): for ( ; ; ) { pthread_mutex_lock(&mutex); g_avail++; // 生产 pthread_cond_signal(&cond); // 通知至少一个等待的消费者 pthread_mutex_unlock(&mutex); } 这样,当没有产品时,消费者线程会阻塞在pthread_cond_wait上,不会占用CPU资源。 条件变量的其他重要函数: - pthread_cond_broadcast: 唤醒所有等待该条件变量的线程。 - pthread_cond_timedwait: 带有超时的等待。 销毁条件变量pthread_cond_destroy(&cond); 注意事项: 1. 条件变量必须与互斥配合使用,且该互斥必须保护着条件判断中的共享变量。 2. 在调用pthread_cond_wait前,必须确保已经获取了互斥。 3. 在等待条件时,必须使用循环检查条件,避免虚假唤醒。 引用[4]中提到了线程同步的重要性,条件变量正是实现线程同步的关键机制之一。 下面是一个完整的使用条件变量的示例(生产者-消费者模型):</think>### pthread 条件变量使用指南与工作原理 #### 一、条件变量的核心作用 条件变量是**线程同步的关键机制**,用于解决"等待特定条件成立"的场景。当线程需要等待某个条件满足时,通过条件变量进入阻塞状态,避免忙等待(busy-waiting)消耗CPU资源[^4]。主要解决以下问题: 1. 生产者-消费者模型中的资源等待 2. 线程间的任务协调 3. 避免轮询造成的CPU浪费 #### 二、基本使用流程 ##### 1. 初始化与销毁 ```c pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 静态初始化 // 或动态初始化 pthread_cond_init(&cond, NULL); // 使用后销毁 pthread_cond_destroy(&cond); ``` ##### 2. 等待条件(消费者端) ```c pthread_mutex_lock(&mutex); while (condition == false) { // 必须用while循环检查条件 pthread_cond_wait(&cond, &mutex); // 原子操作:解+等待 } // 条件满足后执行操作 pthread_mutex_unlock(&mutex); ``` **关键点**: - 必须与互斥配合使用 - 使用`while`而非`if`防止虚假唤醒(spurious wakeup) - `pthread_cond_wait()`会自动释放并阻塞线程 ##### 3. 通知条件(生产者端) ```c pthread_mutex_lock(&mutex); // 改变条件状态 condition = true; // 通知等待线程 pthread_cond_signal(&cond); // 唤醒至少一个线程 // 或 pthread_cond_broadcast(&cond); // 唤醒所有线程 pthread_mutex_unlock(&mutex); ``` #### 三、生产者-消费者模型完整示例 ```c #include <pthread.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; int product_count = 0; // 共享资源 // 消费者线程 void* consumer(void* arg) { while(1) { pthread_mutex_lock(&mutex); while(product_count == 0) { // 无产品时等待 pthread_cond_wait(&cond, &mutex); } product_count--; // 消费产品 printf("Consumed. Stock: %d\n", product_count); pthread_mutex_unlock(&mutex); } return NULL; } // 生产者线程 void* producer(void* arg) { while(1) { pthread_mutex_lock(&mutex); product_count++; // 生产产品 printf("Produced. Stock: %d\n", product_count); pthread_cond_signal(&cond); // 通知消费者 pthread_mutex_unlock(&mutex); sleep(1); // 生产间隔 } return NULL; } int main() { pthread_t prod_thread, cons_thread; pthread_create(&prod_thread, NULL, producer, NULL); pthread_create(&cons_thread, NULL, consumer, NULL); pthread_join(prod_thread, NULL); pthread_join(cons_thread, NULL); } ``` #### 四、核心工作原理 1. **阻塞等待机制** `pthread_cond_wait()`执行时: - 原子性地释放关联的互斥 - 线程进入阻塞状态,加入条件变量的等待队列 - 被唤醒后自动重新获取互斥 2. **唤醒机制** ```mermaid graph LR A[生产者修改条件] --> B[pthread_cond_signal] B --> C{条件变量等待队列} C --> D[唤醒一个线程] D --> E[线程进入就绪状态] ``` 3. **虚假唤醒处理** 系统可能无故唤醒线程(与硬件中断相关),因此必须用`while`循环重复检查条件: ```c while(!condition) // 正确做法 pthread_cond_wait(); ``` #### 五、关键注意事项 1. **的持有时间** 在发送信号时**必须持有**,防止条件变量在检查条件和进入等待之间被修改[^4]。 2. **通知函数选择** - `pthread_cond_signal()`:至少唤醒一个等待线程 - `pthread_cond_broadcast()`:唤醒所有等待线程(广播) > 广播适用于多个线程等待不同条件的情况 3. **超时控制** 使用`pthread_cond_timedwait()`实现带超时的等待: ```c struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += 5; // 设置5秒超时 pthread_cond_timedwait(&cond, &mutex, &ts); ``` 4. **资源管理** 确保在销毁条件变量前没有线程在等待: ```c pthread_cond_destroy(&cond); // 返回EBUSY表示有线程等待 ``` #### 六、典型应用场景 1. 线程池任务调度 2. 有限资源访问控制 3. 事件驱动系统 4. 读写实现(通过条件变量跟踪读写状态) > 通过合理使用条件变量,可构建高效线程同步机制,避免引用[3]中抢票示例出现的竞争条件问题[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值