161 Linux C++ 通讯架构实战15,线程池代码分析

线程池应该使用的地方  和 epoll 技术结合

线程池代码处理数据的地方。

线程池分析:

线程池代码1 threadpool_create

//Tencent8888 start threadpool_create函数的目的初始化线程池,对应的struct是 threadpool_t

/*

    1.先malloc整个线程池的大小

    2.这里使用的 do while实现的好处是,如果哪里有问题,则可以直接break出来,这里要学习的是这种技巧,实际上是为了避免使用goto 语句

    3.锁子和 条件变量的初始化

    4.将存放线程池中每个线程的tid。数组 进行分配空间。pthread_t *threads

    5.将 任务队列 task_queue 进行分配空间

    6.对于最小数量的thread,进行初始化

    7.对管理线程进行初始化

    8.如果有有问题 free相关的信息

    9.下来我们就要看 thread 的初始化的回调函数 threadpool_thread

    10.然后再看 管理线程 的初始化的回调函数 adjust_thread

*/

//Tencent8888 end

//Tencent8888 start threadpool_create函数的目的初始化线程池,对应的struct是 threadpool_t
/*
    1.先malloc整个线程池的大小
    2.这里使用的 do while实现的好处是,如果哪里有问题,则可以直接break出来,这里要学习的是这种技巧,实际上是为了避免使用goto 语句
    3.锁子和 条件变量的初始化
    4.将存放线程池中每个线程的tid。数组 进行分配空间。pthread_t *threads
    5.将 任务队列 task_queue 进行分配空间
    6.对于最小数量的thread,进行初始化
    7.对管理线程进行初始化
    8.如果有有问题 free相关的信息
    9.下来我们就要看 thread 的初始化的回调函数 threadpool_thread
    10.然后再看 管理线程 的初始化的回调函数 adjust_thread
*/
//Tencent8888 end

threadpool_t *threadpool_create(int min_thr_num, int max_thr_num, int queue_max_size)
{
    int i;
    threadpool_t *pool = NULL;
    do {
        if((pool = (threadpool_t *)malloc(sizeof(threadpool_t))) == NULL) {  
            printf("malloc threadpool fail");
            break;/*跳出do while*/
        }

        pool->min_thr_num = min_thr_num;                /* 线程池最小线程数 */
        pool->max_thr_num = max_thr_num;                /* 线程池最大线程数 */
        pool->busy_thr_num = 0;                         /* 忙状态线程个数 */
        pool->live_thr_num = min_thr_num;               /* 活着的线程数 初值=最小线程数 */
        pool->queue_size = 0;                           /* 有0个产品 */
        pool->queue_max_size = queue_max_size;          /* task_queue队列可容纳任务数上限*/
        pool->queue_front = 0;                          /* task_queue队头下标 */
        pool->queue_rear = 0;                           /* task_queue队尾下标 */
        pool->shutdown = false;                         /* 不关闭线程池 */

        /* 根据最大线程上限数, 给工作线程数组开辟空间, 并清零 */
        pool->threads = (pthread_t *)malloc(sizeof(pthread_t)*max_thr_num); 
        if (pool->threads == NULL) {
            printf("malloc threads fail");
            break;
        }
        memset(pool->threads, 0, sizeof(pthread_t)*max_thr_num);

        /* 队列开辟空间 */
        pool->task_queue = (threadpool_task_t *)malloc(sizeof(threadpool_task_t)*queue_max_size);
        if (pool->task_queue == NULL) {
            printf("malloc task_queue fail");
            break;
        }

        /* 初始化互斥琐、条件变量 */
        if (pthread_mutex_init(&(pool->lock), NULL) != 0
                || pthread_mutex_init(&(pool->thread_counter), NULL) != 0
                || pthread_cond_init(&(pool->queue_not_empty), NULL) != 0
                || pthread_cond_init(&(pool->queue_not_full), NULL) != 0)
        {
            printf("init the lock or cond fail");
            break;
        }

        /* 启动 min_thr_num 个 work thread */
        for (i = 0; i < min_thr_num; i++) {
            pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void *)pool);/*pool指向当前线程池*/
            printf("start thread 0x%x...\n", (unsigned int)pool->threads[i]);
        }
        pthread_create(&(pool->adjust_tid), NULL, adjust_thread, (void *)pool);/* 启动管理者线程 */

        return pool;

    } while (0);

    threadpool_free(pool);      /* 前面代码调用失败时,释放poll存储空间 */

    return NULL;
}

线程池代码2 threadpool_thread

代码中的使用

pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void *)pool);//pool指向当前线程池

pthread_create的第一个参数为传输参数,保存系统给我们分配好的线程ID。

第二个参数为NULL。表示使用线程默认属性

第三个参数为函数指针,指向线程主函数,该函数执行完毕,则线程结束

第四个参数 为第三个函数指针需要的参数,当前传递的是整个线程池pool

先复习一下 pthread_create函数 的使用

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

返回值:成功:0; 失败:错误号 -----Linux环境下,所有线程特点,失败均直接返回错误号。

参数:

pthread_t:当前Linux中可理解为:typedef  unsigned long int  pthread_t;

参数1:传出参数,保存系统为我们分配好的线程ID

参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。

参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。

参数4:线程主函数执行期间所使用的参数。

下来看threadpool_thread 这个方法到底干了些啥?

1.先是将参数 threadpool 取出来,因为pthread_create使用的时候,会将整个线程池poll做为参数传递过来。这是一种poll使用的方法

pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void *)pool);//pool指向当前线程池

/* 线程池中各个工作线程 */
void *threadpool_thread(void *threadpool)
{
    threadpool_t *pool = (threadpool_t *)threadpool;
    threadpool_task_t task;

    while (true) {
        /* Lock must be taken to wait on conditional variable */
        /*刚创建出线程,等待任务队列里有任务,否则阻塞等待任务队列里有任务后再唤醒接收任务*/
        pthread_mutex_lock(&(pool->lock));

        /*queue_size == 0 说明没有任务,调 wait 阻塞在条件变量上, 若有任务,跳过该while*/
        while ((pool->queue_size == 0) && (!pool->shutdown)) {  
            printf("thread 0x%x is waiting\n", (unsigned int)pthread_self());
            pthread_cond_wait(&(pool->queue_not_empty), &(pool->lock));

            /*清除指定数目的空闲线程,如果要结束的线程个数大于0,结束线程*/
            if (pool->wait_exit_thr_num > 0) {
                pool->wait_exit_thr_num--;

                /*如果线程池里线程个数大于最小值时可以结束当前线程*/
                if (pool->live_thr_num > pool->min_thr_num) {
                    printf("thread 0x%x is exiting\n", (unsigned int)pthread_self());
                    pool->live_thr_num--;
                    pthread_mutex_unlock(&(pool->lock));
                    pthread_exit(NULL);
                }
            }
        }

        /*如果指定了true,要关闭线程池里的每个线程,自行退出处理*/
        if (pool->shutdown) {
            pthread_mutex_unlock(&(pool->lock));
            printf("thread 0x%x is exiting\n", (unsigned int)pthread_self());
            pthread_exit(NULL);     /* 线程自行结束 */
        }

        /*从任务队列里获取任务, 是一个出队操作*/
        task.function = pool->task_queue[pool->queue_front].function;
        task.arg = pool->task_queue[pool->queue_front].arg;

        pool->queue_front = (pool->queue_front + 1) % pool->queue_max_size;       /* 出队,模拟环形队列 */
        pool->queue_size--;

        /*通知可以有新的任务添加进来*/
        pthread_cond_broadcast(&(pool->queue_not_full));

        /*任务取出后,立即将 线程池琐 释放*/
        pthread_mutex_unlock(&(pool->lock));

        /*执行任务*/ 
        printf("thread 0x%x start working\n", (unsigned int)pthread_self());
        pthread_mutex_lock(&(pool->thread_counter));                            /*忙状态线程数变量琐*/
        pool->busy_thr_num++;                                                   /*忙状态线程数+1*/
        pthread_mutex_unlock(&(pool->thread_counter));
        (*(task.function))(task.arg);                                           /*执行回调函数任务*/
        //task.function(task.arg);                                              /*执行回调函数任务*/

        /*任务结束处理*/ 
        printf("thread 0x%x end working\n", (unsigned int)pthread_self());
        pthread_mutex_lock(&(pool->thread_counter));
        pool->busy_thr_num--;                                       /*处理掉一个任务,忙状态数线程数-1*/
        pthread_mutex_unlock(&(pool->thread_counter));
    }

    pthread_exit(NULL);
}

线程池代码3 adjust_thread

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

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

相关文章

牛客 2024春招冲刺题单 ONT98 牛牛猜节点【中等 斐波那契数列 Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/6a3dfb5be4544381908529dc678ca6dd 思路 斐波那契数列参考答案Java import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规…

Unity与CocosCreator对比学习二

一、锚点与适配 1.在Creator中 适配通过锚点、位置和Widget达到适配目的&#xff1b;锚点是节点在其父节点坐标系中坐标对其点&#xff0c;其x,y范围在[0, 1]之间&#xff1b; 锚点为(0, 0)时在节点自身的左下角&#xff0c;节点坐标指其左下角在父节点中的坐标&#xff1b;锚…

贪心算法|53.最大子序和

力扣题目链接 class Solution { public:int maxSubArray(vector<int>& nums) {int result INT32_MIN;int count 0;for (int i 0; i < nums.size(); i) {count nums[i];if (count > result) {result count;}if (count < 0) count 0;}return result;} …

[AIGC] Spring Filter 过滤器详解

什么是Spring Filter 在Web应用中&#xff0c;Filter&#xff08;过滤器&#xff09;是在Java Servlet规范中的一种组件&#xff0c;它的主要目的是对HTTP请求或者响应进行处理。Spring Filter则是Spring框架对Java原生Filter的封装版本和扩展。 简单来说&#xff0c;Spring …

物联网实战--驱动篇之(二)Modbus协议

目录 一、modbus简介 二、功能码01、02 三、modbus解析 四、功能码03、04 五、功能码05 六、功能码06 七、功能码16 一、modbus简介 我们在网上查阅modbus的资料发现很多很杂&#xff0c;modbus-RTU ASCII TCP等等&#xff0c;还有跟PLC结合的&#xff0c;地址还分1开…

如果在 Ubuntu 系统中两个设备出现两个相同的端口号解决方案

问题描述&#xff1a; 自己的移动机器人在为激光雷达和IMU配置动态指定的端口时&#xff0c;发现激光雷达和深度相机配置的 idVendor 和 idProduct 相同&#xff0c;但是两个设备都具有不同的ttyUSB号&#xff0c;如下图所示 idVendor&#xff1a;代表着设备的生产商ID,由USB设…

并查集学习(836. 合并集合 + 837. 连通块中点的数量)

//得先加集合个数再合并&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 核心代码&#xff1a; int find(int x){//返回父节点if(x ! p[x]) {p[x] find(p[x]);//路径压缩 } //孩子不等于爸爸&#xff0c;就…

springboot+vue学生宿舍物品存放系统tnozt

需求包括&#xff1a; 三个角色&#xff1a;学生&#xff0c;公寓管理员&#xff08;宿舍管理人员&#xff09;&#xff0c;系统管理员。 本系统基于java语言&#xff0c;结合数据库技术&#xff0c;通过面向对象的设计方法&#xff0c;实现学生信息管理、公寓信息管理、物品存…

STM32外部中断编程相关

一&#xff0c;Nested vectored interrupt controller NVIC即嵌套向量中断控制器&#xff0c;它是内核的器件&#xff0c;M3内核都是支持256个中断&#xff0c;其中包含了16系统中断和240个外部中断&#xff0c;并且具有256级的可编程中断设置。芯片厂商一般不会把内核的这些资…

假期别闲着:REST API实战演练之创建Rest API

1、创建实体类&#xff0c;模拟实体对象 创建一个类&#xff0c;模拟数据数据库来存储数据&#xff0c;这个类就叫Person。 其代码如下&#xff1a; package com.restful;public class Person {private String name;private String about;private int birthYear;public Perso…

MacOS下载和安装HomeBrew的详细教程

在MacOS上安装Homebrew的详细教程如下&#xff1a;&#xff08;参考官网&#xff1a;macOS&#xff08;或 Linux&#xff09;缺失的软件包的管理器 — Homebrew&#xff09; 步骤1&#xff1a;检查系统要求 确保你的MacOS版本至少为macOS Monterey (12) (or higher) 或更高版本…

C语言程序编译全流程,从源代码到二进制

源程序 对于一个最简单的程序&#xff1a; int main(){int a 1;int b 2;int c a b;return 0; }预处理 处理源代码中的宏指令&#xff0c;例如#include等 clang -E test.c处理结果&#xff1a; # 1 "test.c" # 1 "<built-in>" 1 # 1 "&…

【力扣】94. 二叉树的中序遍历、144. 二叉树的前序遍历、145. 二叉树的后序遍历

先序遍历&#xff1a;根-左-右中序遍历&#xff1a;左-根-右后序遍历&#xff1a;左-右-根 94. 二叉树的中序遍历 题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3…

【ControlNet v3版本论文阅读】

网络部分最好有LDM或者Stable Diffusion的基础&#xff0c;有基础的话会看的很轻松 Abstract 1.提出了一种网络结构支持额外输入条件控制大型预训练的扩散模型。利用预训练模型学习一组不同的条件控制。 2.ControlNet对于小型&#xff08;<50k&#xff09;或大型&#xff…

vue项目使用element ui

目录 1、创建一个vue项目 2、找到element官网&#xff0c;点击指南&#xff0c;找到安装栏 3、 找到使用包管理器&#xff0c;复制命令 4、在main.js中引入element 5、使用element ui 6、找到App.vue&#xff0c;导入Button.vue文件&#xff0c;保存启动项目 1、创建一个vu…

千字深度理解:什么是电容的直流偏压特性?

原文来自微信公众号&#xff1a;工程师看海&#xff0c;与我联系&#xff1a;chunhou0820 看海原创视频教程&#xff1a;《运放秘籍》 大家好&#xff0c;我是工程师看海&#xff0c;很久没发千字长文了&#xff0c;原创文章欢迎点赞分享&#xff01; 电容是电路中最常用的被动…

GIt 删除某个特定commit

目的 多次commit&#xff0c;想删掉中间的一个/一些commit 操作方法 一句话说明&#xff1a;利用rebase命令的d表示移除commit的功能&#xff0c;来移除特定的commit # 压缩这3次commit,head~3表示从最近1次commit开始&#xff0c;前3个commit git rebase -i head~3rebase…

nest路由通配符使用

写法 路由路径 ‘ab*cd’ 将匹配 abcd 、ab_cd 、abecd 等。 src/cats/cats.controller.ts import { Controller, Get } from nestjs/common;Controller(cats) export class CatsController {Get(ab*cd)findAll1(): string {return 测试路由通配符;} }效果展示 截图1 截图2 …

蓝桥杯python组真题练习2

目录 1.裁纸刀 2.路径 3.排列字母 4.直线 5.纸张尺寸 1.裁纸刀 11.裁纸刀 - 蓝桥云课 (lanqiao.cn) import os import sys# 请在此输入您的代码sum 4 sum 19 sum 21*20 print(sum) 2.路径 12.路径 - 蓝桥云课 (lanqiao.cn) 这道题涉及到求两个数的最小公倍数。一开始…

企业如何设计和实施有效的网络安全演练?

现实世界中&#xff0c;武装部队一直利用兵棋推演进行实战化训练&#xff0c;为潜在的军事冲突做准备。随着当今的数字化转型&#xff0c;同样的概念正在以网络安全演习的形式在组织中得到应用&#xff0c;很多企业每年都会基于合理的网络攻击场景和事件响应做一些测试和模拟。…