条件变量
阻塞线程
不是什么时候都能阻塞线程
链表头节点
Node*head = NULL;
while(head == NULL)
{
//我们想让代码在这个位置阻塞
//等待链表中有了节点之后再继续向下运行
//使用到了后面要讲的条件变量‐阻塞线程
}
//链表不为空的处理代码
xxxx
1.条件变量是锁吗?
不是锁,但是条件变量能够阻塞线程
使用条件变量+互斥量
互斥量:保护一块共享数据
条件变量:引起阻塞
生产者和消费者模型
2.条件变量的两个动作
条件不满足,阻塞线程
当条件满足,通知阻塞的线程开始工作
3.条件变量的类型
pthread_cond_t cond ;
conditon 条件
4.主要函数:
①初始化一个条件变量
pthread_cond_init(pthread_cond_t * restrict cond, const pthread_condattr_t * restrict attr );
②销毁一个条件变量
pthread_cond_destroy(pthread_cond_t * cond);
③阻塞等待一个条件变量
pthread_cond_wait( pthread_cond_t *restrict cond, pthread_mutex_t * restrict mutex );
阻塞线程
将已经上锁的mutex解锁
该函数解除阻塞,对互斥锁加锁
④限时等待一个条件变量
pthread_cond_timedwait( pthread_cond_t * restrict cond, pthread_mutex_t * restrict mutex, const struct timespec * restrict abstime );
⑤唤醒至少一个阻塞在条件变量上的线程
pthread_cond_signal(pthread_cond_t* cond);
⑥唤醒全部阻塞在条件变量上的线程
pthread_cond_broadcast(pthread_cond_t * cond);

5、练习
使用条件变量实现生产者,消费者模型
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
typedef struct node
{
int data;
struct node* next;
}Node;
//create head node
Node* head = NULL;
//create mutex
pthread_mutex_t mutex;
//create cond
pthread_cond_t cond;
void* produce(void* arg)
{
while(1)
{
//create node
Node* pnew = (Node*)malloc(sizeof(Node));
//init node
pnew->data = rand()%1000;
//lock
pthread_mutex_lock(&mutex);
pnew->next = head;
head = pnew;
printf("produce:%ld,%d\n",pthread_self(),pnew->data);
//unlock
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
sleep(rand()%3);
}
return NULL;
}
void* customer(void* arg)
{
while(1)
{
//lock
pthread_mutex_lock(&mutex);
if(head == NULL)
{
//continue;
pthread_cond_wait(&cond,&mutex);
}
//delete head node
Node* pdel = head;
head = head->next;
printf("customer:%ld,%d\n",pthread_self(),pdel->data);
free(pdel);
//unlock
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main()
{
pthread_t p1,p2;
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
pthread_create(&p1,NULL,produce,NULL);
pthread_create(&p2,NULL,customer,NULL);
pthread_join(p1,NULL);
pthread_join(p2,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
这是 【多线程 + 互斥锁 + 条件变量 + 生产者消费者模型】 最标准的代码!
生产者线程:不断创建链表节点,头插法加入链表。 消费者线程:不断从链表头删除节点。
解决了 3 个问题:
- 多线程操作共享链表 → 加互斥锁保护
- 链表空了,消费者不浪费 CPU → 条件变量等待
- 生产者生产完,通知消费者来取 → 条件变量唤醒
①链表结构体
typedef struct node
{
int data; // 数据域
struct node* next; // 指针域,指向下一个节点
}Node;
作用:定义一个"节点"小盒子
②全局链表头(共享资源)
Node* head = NULL;
所有线程共享,必须加锁保护
③互斥锁+条件变量
pthread_mutex_t mutex; // 互斥锁:保证操作链表安全
pthread_cond_t cond; // 条件变量:控制等待与唤醒
- 互斥锁 mutex
- 保证同一时间只有一个线程操作链表
- 防止数据错乱、程序崩溃
- 条件变量 cond
- 当链表为空 时,消费者阻塞等待
- 生产者生产后,发送信号唤醒消费者
- 避免消费者空循环浪费 CPU
④生产者线程(produce)
void* produce(void* arg)
{
while(1) // 无限循环生产
{
// 1. 创建新节点
Node* pnew = (Node*)malloc(sizeof(Node));
pnew->data = rand()%1000; // 随机数 0~999
加锁(操作共享资源前必须加锁)
pthread_mutex_lock(&mutex);
链表头插法
pnew->next = head;
head = pnew;
解锁(操作完必须解锁)
pthread_mutex_unlock(&mutex);
条件变量:唤醒等待的消费者,告诉消费者,我生产好了,快来消费
pthread_cond_signal(&cond);
随机休息
sleep(rand()%3); // 随机休息
}
return NULL;
}
⑤消费者线程
void* customer(void* arg)
{
while(1) // 无限循环消费
{
// 🔥 加锁
pthread_mutex_lock(&mutex);
如果链表为空,等待(不浪费CPU)
if(head == NULL)
{
pthread_cond_wait(&cond,&mutex);
}
pthread_cond_wait 做了 3 件事:
- 阻塞等待,直到被唤醒
- 自动解锁 mutex(让生产者可以生产)
- 被唤醒后 自动重新加锁
→ 这是条件变量最核心的机制!
链表头删法
Node* pdel = head;
head = head->next;
printf("customer:%ld,%d\n",pthread_self(),pdel->data);
free(pdel); // 释放节点
解锁
pthread_mutex_unlock(&mutex);
}
return NULL;
}
⑥main函数
int main()
{
pthread_t p1,p2;
// 初始化锁和条件变量
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
// 创建线程
pthread_create(&p1,NULL,produce,NULL);
pthread_create(&p2,NULL,customer,NULL);
// 等待线程
pthread_join(p1,NULL);
pthread_join(p2,NULL);
// 销毁锁和条件变量
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
运行结果

都是先生产再消费