linux——条件变量

条件变量

阻塞线程

不是什么时候都能阻塞线程

复制代码
链表头节点  
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 个问题:

  1. 多线程操作共享链表 → 加互斥锁保护
  2. 链表空了,消费者不浪费 CPU → 条件变量等待
  3. 生产者生产完,通知消费者来取 → 条件变量唤醒

①链表结构体

复制代码
typedef struct node
{
    int data;               // 数据域
    struct node* next;      // 指针域,指向下一个节点
}Node;

作用:定义一个"节点"小盒子

②全局链表头(共享资源)

复制代码
Node* head = NULL;

所有线程共享,必须加锁保护

③互斥锁+条件变量

复制代码
pthread_mutex_t mutex;      // 互斥锁:保证操作链表安全
pthread_cond_t cond;        // 条件变量:控制等待与唤醒
  1. 互斥锁 mutex
  • 保证同一时间只有一个线程操作链表
  • 防止数据错乱、程序崩溃
  1. 条件变量 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 件事:

  1. 阻塞等待,直到被唤醒
  2. 自动解锁 mutex(让生产者可以生产)
  3. 被唤醒后 自动重新加锁

→ 这是条件变量最核心的机制!

链表头删法

复制代码
        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;
}

运行结果

都是先生产再消费

相关推荐
si莉亚1 小时前
ROS2安装EVO工具包
linux·开发语言·c++·开源
Tingjct1 小时前
Linux常用指令
linux·运维·服务器
广州灵眸科技有限公司1 小时前
为RK3588注入澎湃算力:RK1820 AI加速卡完整适配与评测指南
linux·网络·人工智能·物联网·算法
IT界的老黄牛1 小时前
Linux 压缩命令实战:tar、gzip、bzip2、xz、zstd 怎么选?一篇讲清楚
linux·运维·服务器
IT WorryFree2 小时前
飞塔防火墙与第三方设备进行IPSEC故障诊断期间,用户可能会观察到以下错误:
linux·服务器·网络
12345,catch a tiger2 小时前
虚拟机ubuntu安装Vmware Tools
linux·运维·ubuntu
凉、介2 小时前
别再把 PCIe 的 inbound/outbound、iATU 和 eDMA 混为一谈
linux·笔记·学习·嵌入式·pcie
辰风沐阳2 小时前
OpenClaw 安装教程(Ubuntu 24.04 Desktop)
linux·ubuntu
嘿嘿嘿x33 小时前
Linux记录过程
linux·开发语言
程序猿编码4 小时前
一个授予普通进程ROOT权限的Linux内核级后门:原理与实现深度解析
linux·运维·服务器·内核·root权限