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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【错误记录】jupyter notebook打开后服务器错误Forbidden问题

如题&#xff0c;在Anaconda Prompt里输入jupyter notebook后可以打开浏览器&#xff0c;但打开具体项目后就会显示“服务器错误&#xff1a;Forbidden”&#xff0c;终端出现&#xff1a; tornado.web.HTTPError: HTTP 403: Forbidden 查看jupyter-server和jupyter notebook版…

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

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

PostgreSQL实现透视表查询

PostgreSQL 8.3版本发布时&#xff0c;引入了一个名为tablefunc的新扩展。这个扩展提供了一组非常有趣的函数。其中之一是交叉表函数&#xff0c;用于创建数据透视表。这就是我们将在本文中讨论的内容。 需求说明 解释此函数如何工作的最简单方法是使用带有数据透视表的示例…

使用Tauri创建桌面应用

当前是在 Windows 环境下 1.准备 系统依赖项 Microsoft C 构建工具WebView2 (Windows10 v1803 以上版本不用下载&#xff0c;已经默认安装了) 下载安装 Rust下载安装 Rust 需要重启终端或者系统 重新打开cmd&#xff0c;键入rustc --version&#xff0c;出现 rust 版本号&…

【掩体计划——DFS+缩点】

题目 代码 #include <bits/stdc.h> using namespace std; const int N 1e5 10; vector<vector<int>> g; bool st[N]; int ans 1e9; bool dfs(int f, int u, int dis) {bool is 1;for (auto j : g[u]){if (j f)continue;is & dfs(u, j, dis (g[u].…

第四十四篇 EfficientNetV1、V2模型详解

摘要 EfficientNetV1 详解 简要介绍 EfficientNet是Google提出的一种高效的神经网络架构,其核心思想是通过比例缩放网络的宽度(通道数)、高度和深度(层数)来平衡计算资源和准确性。EfficientNetV1是该系列的首个版本,在提出时便在效果、参数量、速度方面均大幅超越了之…

微信小程序踩坑指南(二)<template>和<block>

<template> 小程序里的和Vue里的表达的不是一种含义。小程序的template是一种模板&#xff0c;不能用于直接显示代码。它正常情况下不显示&#xff0c;需加载使用。 <block> 并不是一个组件&#xff0c;它仅仅是一个包装元素&#xff0c;不会在页面中做任何渲染…

【Spring】注解开发

为了提高开发效率&#xff0c;从 Spring 2.0 开始引入了多种注解&#xff0c;而在 Spring 3.0 中则实现了纯注解的开发方式。 一、注解的使用 在 Spring 2.0 之后&#xff0c;使用注解进行开发主要分为两个步骤&#xff1a; 定义 Bean&#xff1a;使用 Component 注解来定义…

【Linux】vim编辑器练习

1.在/tmp目录下建立一个名为mytest的目录&#xff0c;进入mytest目录 (1)进入/tmp目录 cd /tmp &#xff08;2&#xff09;创建mytest目录 mkdir mytest &#xff08;3&#xff09;查看是否创建成功 ls 或 ls -l &#xff08;4&#xff09;进入mytest目录 cd mytest …

游戏引擎学习第25天

Git: https://gitee.com/mrxiao_com/2d_game 今天的计划 总结和复述&#xff1a; 这段时间的工作已经接近尾声&#xff0c;虽然每次编程的时间只有一个小时&#xff0c;但每一天的进展都带来不少收获。尽管看起来似乎花费了很多时间&#xff0c;实际上这些日积月累的时间并未…

《Python基础》之Pandas库

目录 一、简介 二、Pandas的核心数据结构 1、Series 2、DataFrame 三、数据读取与写入 1、数据读取 2、数据写入 四、数据清洗与处理 1、处理缺失值 2、处理重复值 3、数据转换 五、数据分析与可视化 1、统计描述 2、分组聚合 3、数据可视化 六、高级技巧 1、时…

深入探讨锁升级问题

1. 引言 本文深入探讨锁升级问题。 2. 锁升级问题概述 2.1 锁升级的概念 2.1.1 定义 锁升级是指数据库管理系统将较低粒度的锁&#xff08;如行级锁&#xff09;转换为较高粒度的锁&#xff08;如表级锁&#xff09;的过程。这种情况通常发生在事务对同一对象的多个较低粒…

推荐几个可以免费下载网站模板的资源站

推荐几个可以免费下载网站模板的资源站&#xff0c;上面有免费的wordpress模板和帝国CMS模板可以下载。 模板帝 Mobandi.com 模板帝是一个提供丰富网站模板资源的平台&#xff0c;旨在帮助用户快速构建和美化自己的网站。无论是个人博客、企业官网还是电子商务平台&#xff…

设计模式 更新ing

设计模式 1、六大原则1.1 单一设计原则 SRP1.2 开闭原则1.3 里氏替换原则1.4 迪米特法则1.5 接口隔离原则1.6 依赖倒置原则 2、工厂模式 1、六大原则 1.1 单一设计原则 SRP 一个类应该只有一个变化的原因 比如一个视频软件&#xff0c;区分不同的用户级别 包括访客&#xff0…

c++预编译头文件

文章目录 c预编译头文件1.使用g编译预编译头文件2.使用visual studio进行预编译头文件2.1visual studio如何设置输出预处理文件&#xff08;.i文件&#xff09;2.2visual studio 如何设置预编译&#xff08;初始创建空项目的情况下&#xff09;2.3 visual studio打开输出编译时…

SeggisV1.0 遥感影像分割软件【源代码】讲解

在此基础上进行二次开发&#xff0c;开发自己的软件&#xff0c;例如&#xff1a;【1】无人机及个人私有影像识别【2】离线使用【3】变化监测模型集成【4】个人私有分割模型集成等等&#xff0c;不管是您用来个人学习还是公司研发需求&#xff0c;都相当合适&#xff0c;包您满…

echarts地图立体效果,echarts地图点击事件,echarts地图自定义自定义tooltip

一.地图立体效果 方法1:两层地图叠加 实现原理:geo数组中放入两个地图对象,通过修改zlevel属性以及top,left,right,bottom形成视觉差 配置项参考如下代码: geo: [{zlevel: 2,top: 96,map: map,itemStyle: {color: #091A51ee,opacity: 1,borderWidth: 2,borderColor: #16BAFA…

HTML 快速上手

目录 一. HTML概念 二. HTML标签 1. 标题标签 2. 段落标签 3. 换行标签 4. 图片标签 5. 超链接标签 6. 表格标签 7. 表单标签 7.1 form 标签 7.2 input 标签 (1) 文本框 (2) 单选框 (3) 密码框 (4) 复选框 (5) 普通按钮 (6) 提交按钮 8. select标签 9. 无语义…

Linux 各个目录作用

刚毕业的时候学习Linux基础知识&#xff0c;发现了一份特别好的文档快乐的 Linux 命令行&#xff0c;翻译者是happypeter&#xff0c;作者当年也在慕课录制了react等前端相关的视频&#xff0c;通俗易懂&#xff0c;十分推荐 关于Linux的目录&#xff0c;多数博客已有详细介绍…

Fastapi + vue3 自动化测试平台---移动端App自动化篇

概述 好久写文章了&#xff0c;专注于新框架&#xff0c;新UI界面的实践&#xff0c;废话不多说&#xff0c;开搞 技术架构 后端&#xff1a; Fastapi Airtest multiprocessing 前端&#xff1a; 基于 Vue3、Vite、TypeScript、Pinia、Pinia持久化插件、Unocss 和 Elemen…