LINUX 入门 4

LINUX 入门 4

day6 7 20240429 20240504 耗时:240min

课程链接地址

第4章 LINUX环境编程——实现线程池

C基础

  1. 第3节

    1. #define里面的行不能乱空行,要换行就打\

    2. typedef 是 C 和 C++ 中的一个关键字,用于为已有的数据类型定义一个新的名字。通过 typedef 可以为数据类型起一个更直观或者更易于理解的名字,也可以用来简化复杂的数据类型声明。例如:

      unsigned long long int ULLONG; // 为 unsigned long long int 定义了一个新名字 ULLONG
      
    3. static void *nThreadPoolCallback(void *arg){ }

      nThreadPoolCallback 函数声明为 static 可能是为了将其作用域限制在当前文件内部,避免其它文件中的函数同名冲突,而不是全局作用域的函数。这样做有助于提高代码的可维护性和可读性,因为这个函数只能在当前文件内部被访问和调用,不会被其它文件意外地使用或修改。

  2. 第5节

     struct nWorker *worker = (struct nWorker*) arg;
    

    这行代码创建了一个指向 struct nWorker 结构的指针 worker,并将其初始化为 (struct nWorker*) arg。通常这样的语句在多线程编程中会被用到,其中 arg 是传递给线程的参数,这里将其转换为 struct nWorker* 类型的指针以便在线程中使用。

  3. 第6节

    pthread_cond_signalpthread_cond_broadcast 是 POSIX 线程库中用于线程同步的函数,通常与条件变量(condition variable)一起使用。

    1. pthread_cond_signal 函数用于唤醒一个正在等待条件变量的线程。如果有多个线程正在等待条件变量,那么只会唤醒其中的一个。通常情况下,这是因为某个线程执行了某些操作,使得条件变量的条件满足,因此需要通知等待该条件变量的线程继续执行。
    2. pthread_cond_broadcast 函数用于唤醒所有正在等待条件变量的线程。这个函数会同时唤醒所有等待该条件变量的线程,而不是像 pthread_cond_signal 那样只唤醒其中的一个。通常情况下,这是在某个线程执行了某些操作,使得多个线程都能继续执行时使用。

    这两个函数通常与互斥锁(mutex)一起使用,以实现线程之间的同步。当某个线程需要等待某个条件满足时,它会先释放互斥锁并等待条件变量,而其他线程则可能在条件不满足时进入等待状态。当条件满足时,某个线程会调用 pthread_cond_signalpthread_cond_broadcast 来唤醒等待的线程,让它们继续执行。

1 线程池的使用场景与原理分析

  1. 使用场景:

    1. 百万级的client,server多线程 开线程来同时处理不同client发来的message

      在这里插入图片描述

      but开不了那么多thread,linux下一个posix线程占8M

      1G=1024M开128个最多

      16G内存最多开128*16=2048个thread

    2. 日志文件

      1. disk磁盘操作比memory操作慢不是一个数量级,日志存储到磁盘的文件里,刷新到disk

        处理IO读写,准备好写log里的文字是memory操作,

        log存到文件是disk操作,会引起thread挂起,等IO就绪。 写log任务放到线程池里

  2. 线程池好处

    1. 避免线程太多,内存memory耗尽

    2. 避免反复重复创建和销毁thread, 创建完就放池子,用完还回去

    3. 任务与执行分离(日志写和存储,写内容生成和存到文件)

      例子:银行营业厅,办业务的是任务, 柜员是执行

      在这里插入图片描述

  3. def:

    线程池=

    1. 任务队列
    2. 执行队列
    3. 管理组件 mutex或spinlock加锁 调节任务和执行

2 线程池的结构体定义 threadpool

线程池sdk组件封装software Development Kit

  1. 任务队列:

    任务组成,先定义任务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 线程池初始化实现

多用才能对接口熟悉

函数—— 参数,函数体,返回值

  1. 最终function做成sdk,所以做成API接口给其他人调就行
  2. 初始化struct内四个,除了task
  3. 堆上malloc出来的动态数据,要memset置零,防止内容不确定野指针了
  4. 业务功能(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

打断点:

  1. 在判断的地方加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);

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

国内验签的SSL证书!重要数据绝不出境

SSL证书作为保障网络数据传输安全的重要工具&#xff0c;其作用日益凸显。特别是在国内&#xff0c;随着网络安全法的实施与个人信息保护的加强&#xff0c;国内验证签发的SSL证书更是成为企业与个人在线业务不可或缺的一部分。本文将深入探讨国内验签SSL证书的技术原理、验证流…

【stomp 实战】spring websocket用户消息发送源码分析

这一节&#xff0c;我们学习用户消息是如何发送的。 消息的分类 spring websocket将消息分为两种&#xff0c;一种是给指定的用户发送&#xff08;用户消息&#xff09;&#xff0c;一种是广播消息&#xff0c;即给所有用户发送消息。那怎么区分这两种消息呢?那就是用前缀了…

U盘管控软件,禁止员工用U盘拷贝机密数据,防止信息通过U盘泄露

随着信息技术的不断发展&#xff0c;U盘等便携式存储设备已成为我们日常工作中不可或缺的工具。然而&#xff0c;随着U盘的普及&#xff0c;企业面临的信息泄露风险也在不断增加。为了确保企业的信息安全&#xff0c;许多企业开始采用U盘管控软件&#xff0c;禁止员工使用U盘拷…

内容自动化的进阶之路:Kompas.ai带你走进智能创作时代

在数字化媒体的浪潮中&#xff0c;内容创作和管理正变得越来越复杂和挑战性。为了应对这一挑战&#xff0c;内容自动化技术应运而生&#xff0c;它通过使用人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;算法&#xff0c;自动化内容创作的多个环节&a…

ubuntu安装mysql本地navicat连接使用

ubuntu安装mysql&#xff0c;选择在线安装非常快&#xff1a; 安装 sudo apt install -y mysql-server-8.0先下载资源&#xff08;指定版本下载&#xff09; 如果下不下来&#xff0c;遇到报错多半是 工具需要更新了 sudo apt update更新一下即可&#xff08;sudo就是权限更…

静态分析-RIPS-源码解析记录-02

这部分主要分析scanner.php的逻辑&#xff0c;在token流重构完成后&#xff0c;此时ini_get是否包含auto_prepend_file或者auto_append_file 取出的文件路径将和tokens数组结合&#xff0c;每一个文件都为一个包含require文件名的token数组 接着回到main.php中&#xff0c;此时…

【Linux网络编程】4.TCP协议、select多路IO转换

目录 TCP协议 TCP通讯时序 三次握手 四次挥手 滑动窗口 测试代码1 测试结果 Address already in use解决方法 批量杀进程 测试代码2 测试结果 测试代码4 测试结果 TCP状态转换 主动发起连接请求端 主动关闭连接请求端 被动接收连接请求端 被动关闭连接请求端…

使用Docker安装MySQL5.7.36

拉取镜像并查看 docker pull mysql:5.7.36拉取成功后查看&#xff08;非必须&#xff09; docker images创建并设置宿主机 mysql 配置文件目录和数据文件目录 创建相关文件夹将容器中的mysql数据保存到本地&#xff0c;这样即使容器被删除&#xff0c;数据也不会丢失。 mkd…

【微积分听课笔记】全微分,二元极值,Double Integral

6.6 二元函数的极值_哔哩哔哩_bilibili 此笔记为听课笔记&#xff0c;宋浩老师微积分~ 最近诸事缠身&#xff0c;会有种会不会只做一件事好些。实际上&#xff0c;关键在于动力&#xff0c;我不可能每次都准备充分。动力&#xff0c;分配&#xff0c;这是目前进入大学我正在学…

中仕公考:2024年甘肃兰州事业单位招聘公告

兰州市7个县区和市属48家事业单位计划面向社会公开招聘工作人员536名(管理岗位82名&#xff0c;专业技术岗位447名&#xff0c;工勤技能岗位7名)。 应聘人员年龄要求18周岁以上(2006年5月7日以前出生) 报名&#xff1a;2024年5月11日至5月15日(上午9:00至下午17:00) 资格审查…

第9章 负载均衡集群日常维护

一个设计良好的高可用负载均衡集群&#xff0c;交付使用以后并不能一劳永逸。欲使其高效、稳定、持续对外服务&#xff0c;日常维护必不可少。 对于高可用负载均衡集群来说&#xff0c;有两种类型的维护形式&#xff1a;常规性维护与突发性维护。突发性维护一般指故障处理&…

【每日刷题】Day34

【每日刷题】Day34 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 1047. 删除字符串中的所有相邻重复项 - 力扣&#xff08;LeetCode&#xff09; 2. 1475. 商品折扣后…

俄罗斯副总理暗示欧佩克+或增加原油产量,亚洲早盘油价小幅下跌

在俄罗斯副总理亚历山大诺瓦克暗示欧佩克可能采取行动增加原油产量后&#xff0c;亚洲早盘的油价出现小幅下跌。这一消息引起了市场对原油供给增加的担忧&#xff0c;导致油价走低。 City Index和FOREX.com的市场分析师Fawad Razaqzada表示&#xff0c;虽然原油价格在技术上尚…

JAVA(三)常用类和API

目录 常用类与基础API---String String的内存结构 构造器和常用方法 字符串构建 String与其他结构间的转换 String的常用API 系列1&#xff1a;常用方法 系列2&#xff1a;查找 系列3&#xff1a;字符串截取 系列4&#xff1a;和字符/字符数组相关 系列5&#xff1a;开头…

vitis 2020.1 Up date XSA文件后,编译不通过

原来是可以编译通过的&#xff0c;升级XSA文件后&#xff0c;出现各种问题&#xff0c;pmufw没法编译通过 xpfw_config.h:14:10: fatal error: xparameters.h: No such file or directory Vitis 2020.2 - fatal error: xparameters.h: No such file or directory (xilinx.com)…

vscode与git下载安装

粉丝不过W git下载地址: https://git-scm.com/downloads, 安装git时, 记住你安装Git的路径 vscode下载地址: https://code.visualstudio.com/ 下载完后, 并默认安装好, 你就可以进入配置git的环境变量了, 点击win, 点击设置 在搜索框里搜索, 高级系统设置 点到 高级 , 然后点击…

打造文旅客运标杆!吐鲁番国宾旅汽携苏州金龙升级国宾级出行体验

新疆&#xff0c;这片神秘的大地&#xff0c;从无垠沙漠到高耸天山&#xff0c;从古老丝路到繁华都市&#xff0c;处处都散发着独特的魅力&#xff0c;吸引着四面八方的游客。据新疆维吾尔自治区文化和旅游厅数据显示&#xff0c;刚刚过去的“五一”小长假&#xff0c;新疆全区…

开放原子龙蜥社区 2 大学习赛首批获奖者名单公布

近日&#xff0c;开放原子开源基金会联合龙蜥社区推出「人人都可以参与开源」和「基于 ECS Intel 实例部署 GPT-2 大语言模型」两大学习赛&#xff0c;此赛题长期有效&#xff0c;且有开放原子开源基金会和龙蜥社区共同提供的丰厚双重奖励机制。赛题一经发布&#xff0c;吸引了…

线程池(一)

1.线程池的基本概念 1.1 什么是线程池&#xff1a; 线程池是一种利用池化技术思想来实现的线程管理技术&#xff0c;主要是为了复用线程、便利地管理线程和任务、并将线程的创建和任务的执行解耦开来。我们可以创建线程池来复用已经创建的线程来降低频繁创建和销毁线程所带来的…

EIA预测2024年全球石油市场供求平衡,非欧佩克产油国将抵消欧佩克减产影响

美国能源信息署&#xff08;EIA&#xff09;预测&#xff0c;2024年全球石油市场将达到供求平衡状态&#xff0c;主要原因是非欧佩克国家的原油产量增长将抵消欧佩克近期的减产。根据EIA周二公布的短期能源展望报告&#xff0c;全球原油供应量预计将增加至1.0276亿桶/日。而与此…