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;
}

运行结果

都是先生产再消费

相关推荐
minglie11 小时前
zynq的linux驱动版终端AtShellDrv
linux
欧神附体1233 小时前
在虚拟机搭建本地仓库和网络仓库
linux·运维·服务器
是个西兰花4 小时前
Linux:深入解析Linux线程原理与实现
linux·运维·c++·线程·互斥锁
运维大师4 小时前
【Linux运维极简教程】07-磁盘管理与LVM
linux·运维·服务器
乖巧的妹子4 小时前
刷题总结知识
linux·服务器·windows
拳里剑气5 小时前
Linux:基础IO
linux·运维·服务器·io
小小龙学IT5 小时前
Day 2-3:Linux 文件与目录操作
linux
三8446 小时前
基本Linux命令总结
linux·运维·服务器
hehelm7 小时前
AI 大模型接入 SDK —项目概述
linux·服务器·网络·数据库·c++
mounter6257 小时前
跨越鸿沟:从内核驱动开发到超大规模云端生产环境的思考
linux·驱动开发·linux kernel·kernel