Linux条件变量:线程同步的利器
2026/7/9 18:57:33 网站建设 项目流程

Linux 条件变量:线程同步的利器

条件变量(Condition Variable)是 POSIX 线程(pthread)库中最重要、最常用的线程间同步原语之一。它与互斥锁(mutex)配合使用,主要解决“等待某个条件成立”的场景,是实现生产者-消费者模型线程池任务等待状态变更通知等经典模式的首选工具。

一、条件变量的核心思想

条件变量的核心语义只有一句话:

“某个线程可以挂起(等待),直到另一个线程通知它‘条件可能已经满足了’”

不保存任何状态,只负责唤醒等待

条件变量必须和互斥锁一起使用,这是铁律。

最经典的使用模式(伪代码):

pthread_mutex_tmutex;pthread_cond_tcond;// 等待条件成立pthread_mutex_lock(&mutex);while(条件不满足){pthread_cond_wait(&cond,&mutex);// 释放锁 → 等待 → 被唤醒后重新加锁}...// 条件满足,处理业务pthread_mutex_unlock(&mutex);// 通知等待者pthread_mutex_lock(&mutex);...// 修改条件pthread_cond_signal(&cond);// 唤醒一个// 或 pthread_cond_broadcast(&cond); // 唤醒全部pthread_mutex_unlock(&mutex);

二、条件变量的核心 API(全家桶)

函数作用是否阻塞必须持有锁?典型使用场景
pthread_cond_init初始化条件变量创建时调用
pthread_cond_destroy销毁条件变量不再使用时
pthread_cond_wait等待条件变量被通知(释放锁)核心等待函数
pthread_cond_timedwait带超时的等待需要超时保护的等待
pthread_cond_signal唤醒至少一个等待线程建议持有单消费者或无差别唤醒
pthread_cond_broadcast唤醒所有等待线程建议持有多消费者、状态广播

三、最经典的正确写法(必须掌握)

1. 生产者-消费者模型(单生产者多消费者)
#include<pthread.h>#include<stdio.h>#include<unistd.h>#include<stdlib.h>#defineBUFFER_SIZE10pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER;pthread_cond_tcond_not_full=PTHREAD_COND_INITIALIZER;pthread_cond_tcond_not_empty=PTHREAD_COND_INITIALIZER;intbuffer[BUFFER_SIZE];intcount=0;intin=0,out=0;void*producer(void*arg){for(inti=0;i<20;i++){pthread_mutex_lock(&mutex);// 等待不满while(count==BUFFER_SIZE){pthread_cond_wait(&cond_not_full,&mutex);}buffer[in]=i;in=(in+1)%BUFFER_SIZE;count++;printf("生产: %d (count=%d)\n",i,count);pthread_cond_broadcast(&cond_not_empty);// 通知消费者pthread_mutex_unlock(&mutex);usleep(rand()%100000);}returnNULL;}void*consumer(void*arg){for(inti=0;i<20;i++){pthread_mutex_lock(&mutex);// 等待不空while(count==0){pthread_cond_wait(&cond_not_empty,&mutex);}intdata=buffer[out];out=(out+1)%BUFFER_SIZE;count--;printf("消费: %d (count=%d)\n",data,count);pthread_cond_signal(&cond_not_full);// 通知生产者pthread_mutex_unlock(&mutex);usleep(rand()%200000);}returnNULL;}intmain(){pthread_tprod,cons;pthread_create(&prod,NULL,producer,NULL);pthread_create(&cons,NULL,consumer,NULL);pthread_join(prod,NULL);pthread_join(cons,NULL);return0;}
2. 为什么 while 而不是 if?(虚假唤醒)

虚假唤醒(spurious wakeup)是条件变量的天然特性:

  • 即使没人 signal,线程也可能被系统唤醒
  • 被唤醒后条件不一定成立

因此必须用 while 循环检查条件,这是 POSIX 标准强烈推荐的写法:

while(条件不满足){pthread_cond_wait(&cond,&mutex);}

永远不要用 if

四、条件变量常见误区与正确姿势

误区后果正确做法
用 if 而不是 while 判断条件虚假唤醒导致逻辑错误永远用 while
在 signal/broadcast 后不解锁可能导致被唤醒线程立即再次阻塞先 signal,再 unlock(或一起在锁内)
忘记初始化条件变量未定义行为总是 pthread_cond_init
在不持有锁的情况下 signal可能丢失唤醒建议在持有锁时 signal
使用同一个 cond 变量做多种等待唤醒错乱每种等待条件用独立的 cond
忘记销毁 cond 和 mutex资源泄漏程序结束前 destroy

五、进阶技巧与工程实践

  1. 带超时的等待(防止永久阻塞)
structtimespects;clock_gettime(CLOCK_REALTIME,&ts);ts.tv_sec+=5;// 超时 5 秒pthread_cond_timedwait(&cond,&mutex,&ts);
  1. 多条件等待(经典写法)
pthread_mutex_lock(&mutex);while(!ready||!data_available){if(!ready)pthread_cond_wait(&cond_ready,&mutex);if(!data_available)pthread_cond_wait(&cond_data,&mutex);}
  1. 条件变量 + 互斥锁的最佳实践命名
pthread_mutex_tqueue_mutex;pthread_cond_tqueue_not_empty;pthread_cond_tqueue_not_full;
  1. 线程池中任务等待的典型用法
// 线程池 worker 线程while(1){pthread_mutex_lock(&pool->mutex);while(queue_empty(pool->task_queue)){pthread_cond_wait(&pool->cond,&pool->mutex);}task=dequeue(pool->task_queue);pthread_mutex_unlock(&pool->mutex);task->func(task->arg);}

六、总结:一句话记住条件变量

条件变量不是用来“保存条件”的,而是用来“通知条件可能变了”的。
它必须搭配互斥锁使用,等待时用 while + cond_wait通知时用 signal 或 broadcast

掌握了条件变量 + 互斥锁,你就真正掌握了线程同步的精髓,几乎所有复杂的多线程协作模式(线程池、任务队列、状态机、缓存失效通知、barrier 等)都可以基于它构建。

如果你想看更复杂的例子(比如多生产者多消费者、带优先级的条件等待、实现线程安全的队列、条件变量在实际项目中的封装),可以直接告诉我,我可以继续给出完整代码和详细说明。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询