pthread_cond_wait()用法 - 写的很不错,转载

很久没看APUE,今天一位朋友问道关于一个mutex的问题,又翻到了以前讨论过的东西,为了不让自己忘记,把曾经的东西总结一下。
先大体看下网上很多地方都有的关于pthread_cond_wait()的说明:

 

条件变量   
    
  条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。  
    
  1.   创建和注销   
    
  条件变量和互斥锁一样,都有静态动态两种创建方式,静态方式使用PTHREAD_COND_INITIALIZER常量,如下:     
  pthread_cond_t   cond=PTHREAD_COND_INITIALIZER     
    
  动态方式调用pthread_cond_init()函数,API定义如下:     
  int   pthread_cond_init(pthread_cond_t   *cond,   pthread_condattr_t   *cond_attr)    
    
  尽管POSIX标准中为条件变量定义了属性,但在LinuxThreads中没有实现,因此cond_attr值通常为NULL,且被忽略。   
    
  注销一个条件变量需要调用pthread_cond_destroy(),只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程。API定义如下:    
  int   pthread_cond_destroy(pthread_cond_t   *cond)     
    
  2.   等待和激发   
    
int   pthread_cond_wait(pthread_cond_t   *cond,   pthread_mutex_t   *mutex)   
  int   pthread_cond_timedwait(pthread_cond_t   *cond,   pthread_mutex_t   *mutex,   const   struct   timespec   *abstime)    
    
    
    
  等待条件有两种方式:无条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。  
    
  无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的竞争条件(Race   Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。   
    
  激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。  

 

现在来看一段典型的应用:看注释即可。

[cpp]  view plain  copy
  1. #include <pthread.h>  
  2. #include <unistd.h>  
  3.   
  4. static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;  
  5. static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;  
  6.   
  7. struct node {  
  8. int n_number;  
  9. struct node *n_next;  
  10. } *head = NULL;  
  11.   
  12. /*[thread_func]*/  
  13. static void cleanup_handler(void *arg)  
  14. {  
  15.     printf("Cleanup handler of second thread./n");  
  16.     free(arg);  
  17.     (void)pthread_mutex_unlock(&mtx);  
  18. }  
  19. static void *thread_func(void *arg)  
  20. {  
  21.     struct node *p = NULL;  
  22.   
  23.     pthread_cleanup_push(cleanup_handler, p);  
  24.     while (1) {  
  25.     pthread_mutex_lock(&mtx);           //这个mutex主要是用来保证pthread_cond_wait的并发性  
  26.     while (head == NULL)   {               //这个while要特别说明一下,单个pthread_cond_wait功能很完善,为何这里要有一个while (head == NULL)呢?因为pthread_cond_wait里的线程可能会被意外唤醒,如果这个时候head != NULL,则不是我们想要的情况。这个时候,应该让线程继续进入pthread_cond_wait  
  27.         pthread_cond_wait(&cond, &mtx);         // pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx,然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立而被唤醒,唤醒后,该进程会先锁定先pthread_mutex_lock(&mtx);,再读取资源  
  28.                                                 //用这个流程是比较清楚的/*block-->unlock-->wait() return-->lock*/  
  29.     }  
  30.         p = head;  
  31.         head = head->n_next;  
  32.         printf("Got %d from front of queue/n", p->n_number);  
  33.         free(p);  
  34.         pthread_mutex_unlock(&mtx);             //临界区数据操作完毕,释放互斥锁  
  35.     }  
  36.     pthread_cleanup_pop(0);  
  37.     return 0;  
  38. }  
  39.   
  40. int main(void)  
  41. {  
  42.     pthread_t tid;  
  43.     int i;  
  44.     struct node *p;  
  45.     pthread_create(&tid, NULL, thread_func, NULL);   //子线程会一直等待资源,类似生产者和消费者,但是这里的消费者可以是多个消费者,而不仅仅支持普通的单个消费者,这个模型虽然简单,但是很强大  
  46.     /*[tx6-main]*/  
  47.     for (i = 0; i < 10; i++) {  
  48.         p = malloc(sizeof(struct node));  
  49.         p->n_number = i;  
  50.         pthread_mutex_lock(&mtx);             //需要操作head这个临界资源,先加锁,  
  51.         p->n_next = head;  
  52.         head = p;  
  53.         pthread_cond_signal(&cond);  
  54.         pthread_mutex_unlock(&mtx);           //解锁  
  55.         sleep(1);  
  56.     }  
  57.     printf("thread 1 wanna end the line.So cancel thread 2./n");  
  58.     pthread_cancel(tid);             //关于pthread_cancel,有一点额外的说明,它是从外部终止子线程,子线程会在最近的取消点,退出线程,而在我们的代码里,最近的取消点肯定就是pthread_cond_wait()了。关于取消点的信息,有兴趣可以google,这里不多说了  
  59.     pthread_join(tid, NULL);  
  60.     printf("All done -- exiting/n");  
  61.     return 0;  
  62. }  
<think>我们正在讨论pthread_cond_waitpthread_cond_signal,它们是POSIX线程中用于条件变量的函数。 首先,需要解释这两个函数的作用、用法、注意事项等。 然后,根据规则,在回答的最后提出3-5个相关问题,以“§§相关问题§§”开头,并且禁止换行分散。 现在,开始组织回答: </think>`pthread_cond_wait` 和 `pthread_cond_signal` 是 POSIX 线程库中实现线程同步的核心函数,用于条件变量的操作。以下是详细解析: ### 函数原型 ```c int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_signal(pthread_cond_t *cond); ``` ### 核心机制 1. **`pthread_cond_wait` 工作流程**: - 原子操作:释放 `mutex` 锁 → 线程阻塞 → 被唤醒后重新获取锁 - **必须配合 while 循环** 检查条件(防御虚假唤醒): ```c pthread_mutex_lock(&mutex); while (condition == false) { pthread_cond_wait(&cond, &mutex); } // 执行条件满足后的操作 pthread_mutex_unlock(&mutex); ``` 2. **`pthread_cond_signal` 行为特性**: - 唤醒至少 **一个** 等待该条件变量的线程(唤醒顺序依赖调度策略) - 无等待线程时,信号会 **静默丢弃**(不会累积) - 通常需在持有互斥锁时调用(非强制但推荐) ### 关键区别 | 特性 | `pthread_cond_wait` | `pthread_cond_signal` | |---------------------|------------------------------------------|--------------------------------| | **目的** | 阻塞线程直到条件满足 | 唤醒等待条件的线程 | | **锁操作** | 释放锁 → 阻塞 → 重新获取锁 | 不涉及锁操作(但建议在锁内调用) | | **唤醒范围** | - | 至少唤醒一个线程 | | **虚假唤醒防护** | 必须用 while 循环 | 不涉及 | ### 典型应用场景 **生产者-消费者模型**: ```c // 生产者线程 pthread_mutex_lock(&mutex); buffer[in] = data; // 生产数据 in = (in + 1) % SIZE; pthread_cond_signal(&cond); // 唤醒消费者 pthread_mutex_unlock(&mutex); // 消费者线程 pthread_mutex_lock(&mutex); while (count == 0) { // 缓冲区空 pthread_cond_wait(&cond, &mutex); } data = buffer[out]; // 消费数据 out = (out + 1) % SIZE; pthread_mutex_unlock(&mutex); ``` ### 重要注意事项 1. **死锁风险**: - 调用 `pthread_cond_wait` 前必须持有 `mutex` - 未配对使用可能导致永久阻塞 2. **广播唤醒**: - 需唤醒所有线程时用 `pthread_cond_broadcast()` - 过度使用可能引发"惊群效应" 3. **性能陷阱**: - 条件变量等待/唤醒涉及内核调度(上下文切换开销) - 高频同步场景建议结合无锁队列 4. **优先级反转**: - 低优先级线程持有锁时,可能阻塞高优先级线程 - 可通过优先级继承协议(如 `PTHREAD_PRIO_INHERIT`)缓解
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值