Linux条件变量线程池详解

一、条件变量

        【互斥量】解决了线程间同步的问题,避免了多线程对同一块临界资源访问产生的冲突,同一时刻对临界资源的访问,不论是生产者还是消费者,都需要竞争互斥锁,由此也带来了竞争的问题。即生产者和消费者、消费者和消费者之间时刻都在竞争这把锁,而临界资源是有限的,当临界资源为空候,消费者之间的竞争便没有意义,反而降低了运行效率。

        有没有什么办法可以等生产者线程生产出资源,消费者线程再去竞争锁消费呢?这就是条件变量的作用。

        正如互斥量保护了【临界资源】,条件变量也保护【条件】资源,当条件不符合时即【条件】资源空缺,消费者线程休眠等待;当【条件】资源产生,消费者线程被唤醒然后去消费资源。这样便解决了线程间等待的问题,提高了运行效率。

pthread_cond_t cond  //定义条件变量
pthread_cond_init(&cond) //动态初始化
  
pthread_cond_t cond = PTHREAD_COND_INITIALIZER //静态创建并初始化

/*消费者线程休眠等待生产者线程通知*/
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex)

/*生产者线程生产完资源后发送通知*/
int pthread_cond_signal(pthread_cond_t *cond)    //唤醒一个休眠的线程
int pthread_cond_broadcast(pthread_cond_t *cond) //广播唤醒所有休眠的线程
  • 条件变量的使用要绑定互斥锁

    • 因为条件变量的使用过程中,对于生产者线程,需要产生资源当然要上锁;对于消费者线程,需要消费资源甚至还要判断资源是否为空,也要上锁

  • 【wait】函数的具体动作

    • 进入等待的线程列表中休眠并释放锁

    • 被唤醒后返回,同时上锁

wait函数的每个动作都是【原子操作】,动作连续并且不会被别的程序干扰,意味着CPU调度一定能保证动作粒一气呵成

         消费线程不判断资源是否为空直接等待的话容易丢失signal信号(假设生产线程提前生产出资源也signal了)

        唤醒也可以用broadcast,这种情况消费线程一定要判断资源是否为空,否则链表形式的资源容易出现【段错误】(因为多个线程去争抢资源时,总有抢不到的)

二、线程池 

若干线程的集合,可用结构体来构造

必要性:当会出现大量执行时间短的任务时,甚至这个时间比线程创建+销毁的时间还短,这时候再创建多个线程就不划算了,可以未雨绸缪,构建线程池,提前创建多个线程来节省开销

 线程池的实现过程

1.创建任务队列、线程池的结构体、定义一个线程池

#define pool_num (10)

typedef struct Task{
	struct Task *next;
	void *(*func)(void *);
	void *arg;

}Task;

typedef struct{
	pthread_mutex_t mutex;
	pthread_cond_t cond;
	Task *task_head;
	int busywork;
	pthread_t tid[pool_num];

}ThreadPool;

ThreadPool *pool;

2.初始化线程池、线程的工作

void pool_init()
{
	pool = (ThreadPool *)malloc(sizeof(ThreadPool));
	pthread_mutex_init(&pool->mutex,NULL);
	pthread_cond_init(&pool->cond,NULL);
	pool->task_head = NULL;
	pool->busywork = 0;
	for(i=0;i<pool_num;i++)
	{
		pthread_create(&pool->tid[i],NULL,workthread,NULL);
	}
}

void *workthread(void *arg)
{
	while(1)
	{	
	//	usleep(10000);  //avoid other process couldn't rob the lock and several process repeatedly rob the lock
		pthread_mutex_lock(&pool->mutex);
		while(pool->task_head == NULL)
		{
			pthread_cond_wait(&pool->cond,&pool->mutex);
		}
		Task *ptask = pool->task_head;//任务取走线程池的头个线程
		pool->task_head = ptask->next;//重置线程池的头个线程为下一个
		pool->busywork--;//
		pthread_mutex_unlock(&pool->mutex);
		printf("%ld start task %d\n",pthread_self(),(int)ptask->arg);
		ptask->func(ptask->arg);
	}
}

3.添加任务

void *realwork(void *arg)
{
	usleep(100000);
	printf("finish task %d\n",(int)arg);

}

void add_task(int arg)
{
	pthread_mutex_lock(&pool->mutex);
	while(pool->busywork >= pool_num)
	{
		pthread_mutex_unlock(&pool->mutex);
		usleep(10000);
		pthread_mutex_lock(&pool->mutex);
	}
	pthread_mutex_unlock(&pool->mutex);
    //以上为判断任务数量是否超过线程池数量,超过则不再允许添加任务
	Task *newtask;
	newtask = (Task *)malloc(sizeof(Task));
	newtask->func = realwork;
	newtask->arg = (void *)arg;
	pthread_mutex_lock(&pool->mutex);
	Task *p = pool->task_head;
	if(p == NULL)
	{
		pool->task_head = newtask;
	}else
	{
		while(p->next != NULL)
		{
			p = p->next;
		}
		p->next = newtask;
		pthread_cond_signal(&pool->cond);
		pool->busywork++;
	}
	pthread_mutex_unlock(&pool->mutex);
}	

4.销毁线程池、任务队列、互斥锁、条件变量

void pool_destroy()
{
	Task *p;
	while(pool->task_head != NULL)
	{
		p = pool->task_head;
		pool->task_head = p->next;
		free(p);
	}
	pthread_mutex_destroy(&pool->mutex);
	pthread_cond_destroy(&pool->cond);
	free(pool);
}

示例

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

#define pool_num (10)
int i = 0;

typedef struct Task{
	struct Task *next;
	void *(*func)(void *);
	void *arg;

}Task;

typedef struct{
	pthread_mutex_t mutex;
	pthread_cond_t cond;
	Task *task_head;
	int busywork;
	pthread_t tid[pool_num];

}ThreadPool;

ThreadPool *pool;

void *workthread(void *arg)
{
	while(1)
	{	
	//	usleep(10000);  //avoid other process couldn't rob the lock and several process repeatedly rob the lock
		pthread_mutex_lock(&pool->mutex);
		while(pool->task_head == NULL)
		{
			pthread_cond_wait(&pool->cond,&pool->mutex);
		}
		Task *ptask = pool->task_head;//任务取走线程池的头个线程
		pool->task_head = ptask->next;//重置线程池的头个线程为下一个
		pool->busywork--;//
		pthread_mutex_unlock(&pool->mutex);
		printf("%ld start task %d\n",pthread_self(),(int)ptask->arg);
		ptask->func(ptask->arg);
	}
}

void *realwork(void *arg)
{
	usleep(100000);
	printf("finish task %d\n",(int)arg);

}

void add_task(int arg)
{
	pthread_mutex_lock(&pool->mutex);
	while(pool->busywork >= pool_num)
	{
		pthread_mutex_unlock(&pool->mutex);
		usleep(10000);
		pthread_mutex_lock(&pool->mutex);
	}
	pthread_mutex_unlock(&pool->mutex);
	Task *newtask;
	newtask = (Task *)malloc(sizeof(Task));
	newtask->func = realwork;
	newtask->arg = (void *)arg;
	pthread_mutex_lock(&pool->mutex);
	Task *p = pool->task_head;
	if(p == NULL)
	{
		pool->task_head = newtask;
	}else
	{
		while(p->next != NULL)
		{
			p = p->next;
		}
		p->next = newtask;
		pthread_cond_signal(&pool->cond);
		pool->busywork++;
	}
	pthread_mutex_unlock(&pool->mutex);
}	

void pool_init()
{
	pool = (ThreadPool *)malloc(sizeof(ThreadPool));
	pthread_mutex_init(&pool->mutex,NULL);
	pthread_cond_init(&pool->cond,NULL);
	pool->task_head = NULL;
	pool->busywork = 0;
	for(i=0;i<pool_num;i++)
	{
		pthread_create(&pool->tid[i],NULL,workthread,NULL);
	}

}

void pool_destroy()
{
	Task *p;
	while(pool->task_head != NULL)
	{
		p = pool->task_head;
		pool->task_head = p->next;
		free(p);
	}
	pthread_mutex_destroy(&pool->mutex);
	pthread_cond_destroy(&pool->cond);
	free(pool);
}

int main()
{
	printf("mypid is %d\n",getpid());
	pool_init();
	sleep(5);
	for(i=1;i<=40;i++)
	{
		add_task(i);
	}
	sleep(5);
	pool_destroy();
	while(1)
	{
		printf("now im alone\n");
		sleep(2);
	}	
	
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/928486.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

「Mac畅玩鸿蒙与硬件38」UI互动应用篇15 - 猜数字增强版

本篇将带你实现一个升级版的数字猜谜游戏。相比基础版&#xff0c;新增了计分和历史记录功能&#xff0c;用户可以在每次猜测后查看自己的得分和猜测历史。此功能展示了状态管理的进阶用法以及如何保存和显示历史数据。 关键词 UI互动应用数字猜谜状态管理历史记录用户交互 一…

【IMF靶场渗透】

文章目录 一、基础信息 二、信息收集 三、flag1 四、flag2 五、flag3 六、flag4 七、flag5 八、flag6 一、基础信息 Kali IP&#xff1a;192.168.20.146 靶机IP&#xff1a;192.168.20.147 二、信息收集 Nmap -sP 192.168.20.0/24 Arp-scan -l nmap -sS -sV -p- -…

【C#】书籍信息的添加、修改、查询、删除

文章目录 一、简介二、程序功能2.1 Book类属性&#xff1a;方法&#xff1a; 2.2 Program 类 三、方法&#xff1a;四、用户界面流程&#xff1a;五、程序代码六、运行效果 一、简介 简单的C#控制台应用程序&#xff0c;用于管理书籍信息。这个程序将允许用户添加、编辑、查看…

【Code First】.NET开源 ORM 框架 SqlSugar 系列

.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列【数据事务…

CLIP模型也能处理点云信息

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

配置宝塔php curl 支持http/2 发送苹果apns消息推送

由于宝塔面板默认的php编译的curl未加入http2的支持&#xff0c;如果服务需要使用apns推送等需要http2.0的访问就会失败&#xff0c;所以重新编译php让其支持http2.0 编译方法&#xff1a; 一、安装nghttp2 git clone https://github.com/tatsuhiro-t/nghttp2.git cd nghttp…

单片机学习笔记 12. 定时/计数器_定时

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~单片机学习笔记 5. 数码管静态显示单片机学习笔记 6. 数码管动态显示单片机学习笔记 7. 独立键盘单片机学习笔记 8…

pytest自定义命令行参数

实际使用场景&#xff1a;pytest运行用例的时候&#xff0c;启动mitmdump进程试试抓包&#xff0c;pytest命令行启动的时候&#xff0c;传入mitmdump需要的参数&#xff08;1&#xff09;抓包生成的文件地址 &#xff08;2&#xff09;mitm的proxy设置 # 在pytest的固定文件中…

【排序用法】.NET开源 ORM 框架 SqlSugar 系列

&#x1f4a5; .NET开源 ORM 框架 SqlSugar 系列 &#x1f389;&#x1f389;&#x1f389; 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列…

sscanf与sprintf函数

本期介绍&#x1f356; 主要介绍&#xff1a;sscanf()、sprintf()这对输入/输出函数&#xff0c;并详细讲解了这两个函数的应用场景。 概述&#x1f356; 在C语言的输出和输入库中&#xff0c;有三对及其相似的库函数&#xff1a;printf()、scanf()、fprintf()、fscanf()、spri…

HCIE IGP双栈综合实验

实验拓扑 实验需求及解法 本实验模拟ISP网络结构&#xff0c;R1/2组成国家骨干网&#xff0c;R3/4组成省级网络&#xff0c;R5/6/7组成数据中 心网络。 配置所有ipv4地址&#xff0c;请自行测试直连。 R1 sysname R1 interface GigabitEthernet0/0/0ip address 12.1.1.1 255.…

关于NXP开源的MCU_boot的项目心得

MCU的启动流程细查 注意MCU上电第一个函数运行的就是Reset_Handler函数&#xff0c;下图是表示了这个函数做了啥事情&#xff0c;注意加强一下对RAM空间的段的印象&#xff0c;从上到下是栈&#xff0c;堆&#xff0c;.bss段&#xff0c;.data段。 bootloader的难点 固件完…

[October 2019]Twice SQL Injection

有一个登录框和一个注册页面&#xff0c;题目也说这个是二次注入&#xff0c;那么就用二次注入的payload就行 1 union select database()# //爆库 1 union select group_concat(table_name) from information_schema.tables where table_schemactftraining# //爆表 1 union …

更多开源创新 挑战OpenAI-o1的模型出现和AI个体模拟突破

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

组合问题变式——选数(dfs)

代码随想录听课笔记1——回溯算法-CSDN博客 这是从1&#xff0c;2&#xff0c;3...,n个数字中选出k个数的组合&#xff0c;输出组合的全部可能的代码 //组合&#xff1a;返回1-n中所有个数为k的组合 1,2,3,4 #include<bits/stdc.h> using namespace std; #define MAX 1…

shodan2-批量查找CVE-2019-0708漏洞

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…

css vue vxe-text-ellipsis table 实现多行文本超出隐藏省略

分享 vxe-text-ellipsis table grid 多行文本溢出省略的用法 正常情况下如果需要使用文本超出隐藏&#xff0c;通过 css 就可以完成 overflow: hidden; text-overflow: ellipsis; white-space: nowrap;但是如果需要实现多行文本溢出&#xff0c;就很难实现里&#xff0c;谷歌…

qt QLinearGradient详解

1、概述 QLinearGradient是Qt框架中QGradient的一个子类&#xff0c;用于创建线性渐变效果。线性渐变是一种颜色沿着一条直线平滑过渡到另一种颜色的效果。QLinearGradient允许你定义渐变的起点和终点&#xff0c;以及在这些点之间的颜色变化。你可以使用它来为图形、背景、边…

万字长文解读深度学习——多模态模型BLIP2

&#x1f33a;历史文章列表&#x1f33a; 深度学习——优化算法、激活函数、归一化、正则化 深度学习——权重初始化、评估指标、梯度消失和梯度爆炸 深度学习——前向传播与反向传播、神经网络&#xff08;前馈神经网络与反馈神经网络&#xff09;、常见算法概要汇总 万字长…

使用ESP32通过Arduino IDE点亮1.8寸TFT显示屏

开发板选择 本次使用开发板模块丝印为ESP32-WROOM-32E 开发板库选择 Arduino IDE上型号选择为ESP32-WROOM-DA Module 显示屏选择 使用显示屏为8针SPI接口显示屏 驱动IC为ST7735S 使用库 使用三个Arduino平台库 分别是 Adafruit_GFXAdafruit_ST7735SPI 代码详解 首…