LINUX 入门 4
LINUX 入门 4
day6 7 20240429 20240504 耗时:240min
课程链接地址
第4章 LINUX环境编程——实现线程池
C基础
-
第3节
-
#define里面的行不能乱空行,要换行就打\
-
typedef
是 C 和 C++ 中的一个关键字,用于为已有的数据类型定义一个新的名字。通过typedef
可以为数据类型起一个更直观或者更易于理解的名字,也可以用来简化复杂的数据类型声明。例如:unsigned long long int ULLONG; // 为 unsigned long long int 定义了一个新名字 ULLONG
-
static void *nThreadPoolCallback(void *arg){ }
将
nThreadPoolCallback
函数声明为static
可能是为了将其作用域限制在当前文件内部,避免其它文件中的函数同名冲突,而不是全局作用域的函数。这样做有助于提高代码的可维护性和可读性,因为这个函数只能在当前文件内部被访问和调用,不会被其它文件意外地使用或修改。
-
-
第5节
struct nWorker *worker = (struct nWorker*) arg;
这行代码创建了一个指向
struct nWorker
结构的指针worker
,并将其初始化为(struct nWorker*) arg
。通常这样的语句在多线程编程中会被用到,其中arg
是传递给线程的参数,这里将其转换为struct nWorker*
类型的指针以便在线程中使用。 -
第6节
pthread_cond_signal
和pthread_cond_broadcast
是 POSIX 线程库中用于线程同步的函数,通常与条件变量(condition variable)一起使用。pthread_cond_signal
函数用于唤醒一个正在等待条件变量的线程。如果有多个线程正在等待条件变量,那么只会唤醒其中的一个。通常情况下,这是因为某个线程执行了某些操作,使得条件变量的条件满足,因此需要通知等待该条件变量的线程继续执行。pthread_cond_broadcast
函数用于唤醒所有正在等待条件变量的线程。这个函数会同时唤醒所有等待该条件变量的线程,而不是像pthread_cond_signal
那样只唤醒其中的一个。通常情况下,这是在某个线程执行了某些操作,使得多个线程都能继续执行时使用。
这两个函数通常与互斥锁(mutex)一起使用,以实现线程之间的同步。当某个线程需要等待某个条件满足时,它会先释放互斥锁并等待条件变量,而其他线程则可能在条件不满足时进入等待状态。当条件满足时,某个线程会调用
pthread_cond_signal
或pthread_cond_broadcast
来唤醒等待的线程,让它们继续执行。
1 线程池的使用场景与原理分析
-
使用场景:
-
百万级的client,server多线程 开线程来同时处理不同client发来的message
but开不了那么多thread,linux下一个posix线程占8M
1G=1024M开128个最多
16G内存最多开128*16=2048个thread
-
日志文件
-
disk磁盘操作比memory操作慢不是一个数量级,日志存储到磁盘的文件里,刷新到disk
处理IO读写,准备好写log里的文字是memory操作,
log存到文件是disk操作,会引起thread挂起,等IO就绪。 写log任务放到线程池里
-
-
-
线程池好处
-
避免线程太多,内存memory耗尽
-
避免反复重复创建和销毁thread, 创建完就放池子,用完还回去
-
任务与执行分离(日志写和存储,写内容生成和存到文件)
例子:银行营业厅,办业务的是任务, 柜员是执行
-
-
def:
线程池=
- 任务队列
- 执行队列
- 管理组件 mutex或spinlock加锁 调节任务和执行
2 线程池的结构体定义 threadpool
线程池sdk组件封装software Development Kit
-
任务队列:
任务组成,先定义任务task struct
再任务队列:链表串起tasks 双向链表
//1.定义task struct nTask{void(*task_func)(struct nTask *task);//一个函数指针,指向一个接受 struct nTask* 参数并且没有返回值的函数。这个指针用于表示任务的执行函数。void *user data;//一个指向 void 类型的指针,用于存储任务函数可能需要的额外数据或参数。struct nTask *prev;struct nTask *next; }; // 双向链表
3 线程池的架构分析与实现
1 最底层:系统层,支持层——实现数据结构+宏定义的基本操作
任务队列,执行队列,管理组件
链表(struct)操作:直接用通讯录那一张的宏定义
#define LIST_INSERT(item, list)do{\item->prev = NULL;\item->next = list;\if((list)!= NULL) (list)->prev = item;\(list) = item; \
} while(0)
//二级指针,list要括号(*ppeople)#define LIST_REMOVE(item, list) do { \if (item->prev != NULL) item->prev->next = item->next; \if (item->next != NULL) item->next->prev = item->prev; \if (list == item) list = item->next; \item->prev = item->next = NULL; \
} while(0)//1.定义task
struct nTask{void(*task_func)(struct nTask *task);void *user data;struct nTask *prev;struct nTask *next;
};//2.执行
struct nWorker{pthread_t threadid;struct nWorker *prev;struct nWorker *next;
};//3管理组件 连接worker和task
struct nManager{struct nTask *tasks; //任务队列struct nWorker *workers; //执行队列pthread_mutex_t mutex; //加互斥锁pthread_cond_t cond; //加条件变量,等待满足条件不锁了
};
2 接口层 ,在支持层上包一层
这里管理组件nManager就是threadpool,这里对类型起别名用typedef
4个:创建、销毁、加任务、线程涉及的回调函数
typedef struct nManager{struct nTask *tasks; //任务队列struct nWorker *workers; //执行队列pthread_mutex_t mutex; //加互斥锁pthread_cond_t cond; //加条件变量,等待满足条件不锁了
}ThreadPool;static void *nThreadPoolCallback(void *arg){}int nThreadPoolCreate(ThreadPool*pool, int nworker){}int nThreadPoolDestroy(ThreadPool*pool, int nworker){}int nThreadPoolPushTask(ThreadPool*pool, struct nTask *task){}
4 线程池初始化实现
多用才能对接口熟悉
函数—— 参数,函数体,返回值
- 最终function做成sdk,所以做成API接口给其他人调就行
- 初始化struct内四个,除了task
- 堆上malloc出来的动态数据,要memset置零,防止内容不确定野指针了
- 业务功能(callback)一样,但是实际task任务不同,相当于task是callback的具体实现
// API
int nThreadPoolCreate(ThreadPool*pool, int numWorker){if(pool == NULL) return -1;if(numWorker < 1) numWorker = 1; //运行线程没有,那就默认1个// 2 对struct里4个初始化,task外界扔进来不用pthread_cond_t blank_cond = PHTREAD_COND_INITIALIZER; //定义空白锁的条件变量memcpy(&pool->cond, &blank_cond,sizeof(pthread_cond_t));//blank_cond 中的 pthread_cond_t 结构体的内容复制到 pool->cond 中。pthread_mutex_init(&pool->mutex,NULL);int i = 0;for(int i =0; i < numWorker; i++){struct nWorker *worker = malloc(sizeof(struct nWorker));//创建失败if(worker ==NULL){perror("malloc");return -2;}memset(worker, 0,sizeof(struct worker));worker->manager = pool;//创建线程int ret = pthread_create(worker->threadid, NULL, nThreadPoolCallback, worker); //创建成功返回0,失败1if(ret){perror("pthread_create");free(worker);return -3;}LIST_INSERT(worker, pool->worker);}return 0; //创建成功, callback是业务功能,一样,但是不等同于task任务,但是执行的任务不同}
5 线程池的线程回调函数实现
核心:三件事
判任务队列里有任务
没就等
有就拿出执行,取用户数据
// API
// callback!=task
static void *nThreadPoolCallback(void *arg){struct nWorker *worker = (struct nWorker*) arg;while(1){pthread_mutex_lock(worker->manager->mutex); //对任务加一把锁while(worker->manager->tasks == NULL){//判有任务,没就等,有就拿出执行,取用户数据pthread_cond_wait(&worker->manager->cond, &worker->manager->mutex);}struct nTask *task = worker->manager->tasks;LIST_REMOVE(task, worker->manager->tasks); //把任务队列tasks的首节点task执行完了,移出来pthread_mutex_unlock(worker->manager->mutex);task->task_func(task->user_data);}free(worker);// but没有退出的break的地方,struct nWorker里引入终止标识
}
问题:but没有退出的break的地方,struct nWorker里引入终止标识
//2.执行
struct nWorker{pthread_t threadid;int terminate;struct nManager *manager; //worker需要有manager联系方式struct nWorker *prev;struct nWorker *next;
};
同时
static void *nThreadPoolCallback(void *arg){struct nWorker *worker = (struct nWorker*) arg;while(1){pthread_mutex_lock(worker->manager->mutex); //对任务加一把锁while(worker->manager->tasks == NULL){//判有任务,没就等,有就拿出执行,取用户数据if(worker->terminate) break; //是1就是终止pthread_cond_wait(&worker->manager->cond, &worker->manager->mutex);}if(worker->terminate){// 退出最外层while,先解锁,不然可能死锁了pthread_mutex_unlock(worker->manager->mutex); break; }struct nTask *task = worker->manager->tasks;LIST_REMOVE(task, worker->manager->tasks); //把任务队列tasks的首节点task执行完了,移出来pthread_mutex_unlock(worker->manager->mutex);task->task_func(task->user_data);}free(worker);// but没有退出的break的地方,struct nWorker里引入终止标识
}
6 线程池的任务添加与线程池销毁
terminate置1就退出来了
int nThreadPoolDestroy(ThreadPool*pool, int nworker){struct nWorker *worker = NULL;for(worker = pool->workers; worker != NULL; worker = worker->next){worker->terminate;//都置1}//信号什么时候往下走,广播使所有条件满足让信号往下走pthread_mutex_lock(&pool->mutex);pthread_cond_broadcast(&pool->cond);// 唤醒所有广播和等待用的同一把锁,不会有死锁,什么东西没听懂pthread_mutex_unlock(&pool->mutex);pool->workers= NULL;pool->tasks = NULL;return 0;
}
任务添加
int nThreadPoolPushTask(ThreadPool*pool, struct nTask *task){//通知thread有task来了pthread_mutex_lock(&pool->mutex);LIST_INSERT(task, pool->tasks);//通知条件变量可满足,唤醒一个threadpthread_cond_signal(&pool->cond);pthread_mutex_unlock(&pool->mutex);}
7 线程池代码gdb调试与bug修改
gcc -o threadpool threadpool.c -lpthread
一堆错 有问题, 视频里面没有写main后面的函数!! 根本看不懂了,这里后面没法调
//
// sdk --> debug thread pool#if 1#define THREADPOOL_INIT_COUNT 20
#define TASK_INIT_SIZE 1000void task_entry(struct nTask *task) { //type //struct nTask *task = (struct nTask*)task;int idx = *(int *)task->user_data;printf("idx: %d\n", idx);free(task->user_data);free(task);
}int main(void) {ThreadPool pool = {0};nThreadPoolCreate(&pool, THREADPOOL_INIT_COUNT);// pool --> memset();int i = 0;for (i = 0;i < TASK_INIT_SIZE;i ++) {struct nTask *task = (struct nTask *)malloc(sizeof(struct nTask));if (task == NULL) {perror("malloc");exit(1);}memset(task, 0, sizeof(struct nTask));task->task_func = task_entry;task->user_data = malloc(sizeof(int));*(int*)task->user_data = i;nThreadPoolPushTask(&pool, task);}getchar();}#endif
gdb调试,适合小工程
gcc -o threadpool threadpool.c -lpthread -g
gdb ./threadpool
打断点:
-
在判断的地方加breakpoint,比如while if
有可能出现空指针
b 行数 b 64 (while判断) b 76 (list remove) b 115 (list insert) r(就是run) c(continue) 没有出现视频里的问题
nThreadPoolCreate里少
memset(pool, 0, sizeof(ThreadPool));
b 80 (task->task_func)b 201(pushtask)rcc
报错了 并没有解决了,还是segmentation fault
Thread 3 "threadpool" received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7ffff6fee700 (LWP 12096)] 0x0000000000400f08 in task_entry (task=0x604a80) at threadpool.c:170 170 int idx = *(int *)task->user_data;
太丑了,放弃了,sourceinsight长得太丑了,还卡的一批,用vscode连虚拟机ok了
这节课基本没听懂!!!云里雾里乱起八糟的
最后调出来了是这个问题
nThreadPoolCallback(void *arg)里面的
task->task_func(task->user_data);
改成task->task_func(task);
相关文章:
LINUX 入门 4
LINUX 入门 4 day6 7 20240429 20240504 耗时:240min 课程链接地址 第4章 LINUX环境编程——实现线程池 C基础 第3节 #define里面的行不能乱空行,要换行就打\ typedef 是 C 和 C 中的一个关键字,用于为已有的数据类型定义一个新的名字。…...
Imagine Flash、StyleMamba 、FlexControl、Multi-Scene T2V、TexControl
本文首发于公众号:机器感知 Imagine Flash、StyleMamba 、FlexControl、Multi-Scene T2V、TexControl You Only Cache Once: Decoder-Decoder Architectures for Language Models We introduce a decoder-decoder architecture, YOCO, for large language models, …...
Java Collections.emptyList() 方法详解
前言 在Java开发的日常中,我们常常需要处理集合数据结构,而这其中就免不了要面对“空集合”的场景。传统的做法可能是直接返回 null,但这往往会引入空指针异常的风险,降低了代码的健壮性。幸运的是,Java为我们提供了一…...
Vue前端环境准备
vue-cli Vue-cli是Vue官方提供的脚手架,用于快速生成一个Vue项目模板 提供功能: 统一的目录结构 本地调试 热部署 单元测试 集成打包上线 依赖环境:NodeJs 安装NodeJs与Vue-Cli 1、安装nodejs(已经安装就不用了) node-…...
代码随想录算法训练营第四十二天| 01背包问题(二维、一维)、416.分割等和子集
系列文章目录 目录 系列文章目录动态规划:01背包理论基础①二维数组②一维数组(滚动数组) 416. 分割等和子集①回溯法(超时)②动态规划(01背包)未剪枝版剪枝版 动态规划:01背包理论基…...
故障——蓝桥杯十三届2022国赛大学B组真题
问题分析 这道题纯数学,考察贝叶斯公式 AC_Code #include <bits/stdc.h> using namespace std; typedef pair<int,double> PI; bool cmp(PI a,PI b){if(a.second!b.second)return a.second>b.second;return a.first<b.first; } int main() {i…...
SSD存储基本知识
存储技术随着时间的推移经历了显著变化,新兴的存储介质正逐步挑战已经成为行业标准的硬盘驱动器(HDD)。在众多竞争者中,固态硬盘(SSD)是最广泛采用且最有潜力占据主导地位的——它们速度快、运行安静&#…...
buuctf-misc题目练习二
ningen 打开题目后是一张图片,放进winhex里面 发现PK,PK是压缩包ZIP 文件的文件头,下一步是想办法进行分离 Foremost可以依据文件内的文件头和文件尾对一个文件进行分离,或者识别当前的文件是什么文件。比如拓展名被删除、被附加…...
Nginx rewrite项目练习
Nginx rewrite练习 1、访问ip/xcz,返回400状态码,要求用rewrite匹配/xcz a、访问/xcz返回400 b、访问/hello时正常访问xcz.html页面server {listen 192.168.99.137:80;server_name 192.168.99.137;charset utf-8;root /var/www/html;location / {root …...
2024,AI手机“元年”? | 最新快讯
文 | 伯虎财经,作者 | 铁观音 2024年,小米、荣耀、vivo、一加、努比亚等品牌的AI手机新品如雨后春笋般涌现。因此,这一年也被业界广泛视为AI手机的“元年” 试想,当你轻触屏幕,你的手机不仅响应你的指令,更…...
5月9(信息差)
🌍 可再生能源发电量首次占全球电力供应的三成 🎄马斯克脑机接口公司 Neuralink 计划将 Link 功能扩展至现实世界,实现控制机械臂、轮椅等 马斯克脑机接口公司 Neuralink 计划将 Link 功能扩展至现实世界,实现控制机械臂、轮椅等…...
leetcode203-Remove Linked List Elements
题目 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val val 的节点,并返回 新的头节点 。 示例 1: 输入:head [1,2,6,3,4,5,6], val 6 输出:[1,2,3,4,5] 示例 2: 输入&…...
2024付费进群系统,源码及搭建变现视频课程(教程+源码)
自从我做资源站项目盈利稳定后,我越来越对网站类项目感兴趣了,毕竟很多网站类项目还是需要一定技术门槛的,可以过滤掉一些人,很多新人做项目就只盯着短视频,所以网站类项目也就没那么的卷。 这个付费进群系统…...
深入理解Django:中间件与信号处理的艺术
title: 深入理解Django:中间件与信号处理的艺术 date: 2024/5/9 18:41:21 updated: 2024/5/9 18:41:21 categories: 后端开发 tags: Django中间件信号异步性能缓存多语言 引言 在当今的Web开发领域,Django以其强大的功能、简洁的代码结构和高度的可扩…...
rk3588局域网推流
最近无意间看见在网上有使用MediaMtx插件配合ffmpeg在Windows来进行推流,然后在使用其他软件进行拉流显示数据图像的,既然windows都可以使用 ,我想linux应该也可以,正好手上也有一块RK3588的开发板,就测试了一下&#…...
Android虚拟机机制
目录 一、Android 虚拟机 dalvik/art(6版本后)二、Android dex、odex、oat、vdex、art区别 一、Android 虚拟机 dalvik/art(6版本后) 每个应用都在其自己的进程中运行,都有自己的虚拟机实例。ART通过执行DEX文件可在设…...
【触摸案例-手势解锁案例-按钮高亮 Objective-C语言】
一、我们来说这个self.btns,这个问题啊,为什么不用_btns, 1.我们说,在懒加载里边儿,经常是写下划线啊,_btns,为什么不写,首先啊,这个layoutSubviews:我们第一次,肯定会去执行这个layoutSubviews: 然后呢,去懒加载这个数组, 然后呢,接下来啊,走这一句话, 第一次…...
ChatPPT开启高效办公新时代,AI赋能PPT创作
目录 一、前言二、ChatPPT的几种用法1、通过在线生成2、通过插件生成演讲者模式最终成品遇到问题改进建议 三、ChatPPT其他功能 一、前言 想想以前啊,为了做个PPT,我得去网上找各种模板,有时候还得在某宝上花钱买。结果一做PPT,经…...
【C语言项目】贪吃蛇(上)
个人主页 ~ gitee仓库~ 欢迎大家来到C语言系列的最后一个篇章–贪吃蛇游戏的实现,当我们实现了贪吃蛇之后,我们的C语言就算是登堂入室了,基本会使用了,当然,想要更加熟练地使用还需要多多练习 贪吃蛇 一、目标二、需要…...
LeNet-5上手敲代码
LeNet-5 LeNet-5由Yann LeCun在1998年提出,旨在解决手写数字识别问题,被认为是卷积神经网络的开创性工作之一。该网络是第一个被广泛应用于数字图像识别的神经网络之一,也是深度学习领域的里程碑之一。 LeNet-5的整体架构: 总体…...
javaWeb入门(自用)
1. vue学习 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><script src"https://unpkg.com/vue2"></script> </head> <body><div id"…...
web3风格的网页怎么设计?分享几个,找找感觉。
web3风格的网站是指基于区块链技术和去中心化理念的网站设计风格。这种设计风格强调开放性、透明性和用户自治,体现了Web3的核心价值观。 以下是一些常见的Web3风格网站设计元素: 去中心化标志:在网站的设计中使用去中心化的标志࿰…...
ASP.NET MVC(-)表单的提交、获取表单数据
FromCollection 方式...
[AIGC] 《MyBatis-Plus 结合 Spring Boot 的动态数据源介绍及 Demo 演示》
在现代的 Web 应用开发中,Spring Boot 已经成为了一种流行的框架选择。而 MyBatis-Plus 则为 MyBatis 框架提供了更强大的功能和便利。当它们结合使用时,动态数据源的运用变得更加简单和高效。 动态数据源的概念允许我们在运行时根据不同的条件或需求选…...
【华为OD机试C卷D卷】部门人力分配(C++/Java/Python)
【华为OD机试】-(A卷+B卷+C卷+D卷)-2024真题合集目录 【华为OD机试】-(C卷+D卷)-2024最新真题目录 题目描述 部门在进行需求开发时需要进行人力安排。 当前部门需要完成 N 个需求,需求用 requirements 表述,requirements[i] 表示第 i 个需求的工作量大小,单位:人月。 这部…...
毕业设计:《基于 Prometheus 和 ELK 的基础平台监控系统设计与实现》
前言 《基于 Prometheus 和 ELK 的基础平台监控系统设计与实现》,这是我在本科阶段的毕业设计,通过引入 Prometheus 和 ELK 架构实现企业对指标与日志的全方位监控。并且基于云原生,使用容器化持续集成部署的开发方式,通过 Sprin…...
docker私有仓库部署与管理
一、搭建本地公有仓库 1.1 首先下载registry镜像 docker pull registry 1.2 在daemon.json文件中添加私有镜像仓库地址并重新启动docker服务 vim /etc/docker/daemon.json 1.3 运行registry容器 docker run -itd -v /data/registry:/var/lib/registry -p 5000:5000 --restartal…...
2024第六届济南国际大健康产业博会将于5月27日如期开幕
由山东省城市经济学会、山东省科学养生协会主办的第六届中国(济南)国际大健康产业博览会,将于5月27-29日,在济南黄河国际会展中心盛大举办。 近年来,健康越来越受到大众的重视,在我国经济重要的转型阶段成…...
计算方法实验9:Romberg积分求解速度、位移
任务 输出质点的轨迹 ( x ( t ) , y ( t ) ) , t ∈ { 0.1 , 0.2 , 0.3 , . . . , 10 } (x(t), y(t)), t\in \{0.1, 0.2, 0.3, ..., 10\} (x(t),y(t)),t∈{0.1,0.2,0.3,...,10},并在二维平面中画出该轨迹.请比较M分别取4, 8, 12, 16, 20 时,Romberg积分达…...
设计模式有哪些基本原则
目录 开闭原则(Open Closed Principle) 里氏替换原则(Liskov Substitution principle) 单一职责原则(Single Responsibility Principle,SRP)...
建设银行官网官方网站/怎样无货源开网店
第1部分 又一次认识C语言makefile文件的书写及应用【文章摘要】makefile用于Linux下整个project的编译。对于Linux下的C/C语言的编译是至关重要的。本文以实际的C源程序为样例。介绍怎样使用makefile来编译Linux下的C语言project,为相关开发工作的开展提供了參考。【…...
wordpress 上传任意附件/搜狐视频
如何判断R为第几范式? 已知一个关系模式的属性之间的语义,也就是相互依赖的关系,如何判断该模式满足第几范式? 1、首先要通过语义把属性之间的函数依赖关系列出来, 2、然后确定哪些属性组合可以候选码,从…...
做排行网站/深圳营销型网站
代码:Tlist 出来的结果,发现有些函数没有显示,只在百度搜到一个相关帖子。那哥们在帖子中写道:“我又试了几个类似的,貎似不能生成函数声明的tag,这也会影响到C-X C-O的补全效果。 通读了一遍ctags的documentÿ…...
网站是怎么做排名的/做网页设计一个月能挣多少
<?php include_once smarty.php; $smarty->assign(title,标题); $smarty->assign(content,内容);$output $smarty->fetch(index.html); echo $output;// $smarty->display(index.html); ?>#使用fetch函数,你可以将要输出的html赋值给一个变量&…...
网站开发怎么谈/seo超级外链工具免费
爬取网站时经常会遇到需要登录的问题,这是就需要用到模拟登录的相关方法。python提供了强大的url库,想做到这个并不难。这里以登录学校教务系统为例,做一个简单的例子。 首先得明白cookie的作用,cookie是某些网站为了辨别用户身份…...
曲靖做网站需要多少钱/荥阳网络推广公司
Java Vector 类Vector类实现了一个动态数组。和ArrayList和相似,但是两者是不同的:Vector是同步访问的。Vector包含了许多传统的方法,这些方法不属于集合框架。Vector主要用在事先不知道数组的大小,或者只是需要一个可以改变大小的…...