手写线程池|C语言版(二)|定义线程池的结构、创建线程池实例

文章目录

  • 定义线程池结构
    • 任务结构体
    • 定义线程池结构体
  • 组织头文件
  • 创建线程池实例
    • 函数原型定义
    • 线程池创建函数实现
      • 初始化线程池结构体指针
      • 初始化线程池结构体的各类参数
  • 定义线程池的结构C代码
  • 创建线程池总体C代码

本文中,我们将创建线程池的结构体,该结构体中包含了线程池所需要的全部数据类型,并且我们实现了创建线程池函数

前文回顾:

手写线程池|C语言版(一)|线程池的定义和运行逻辑

定义线程池结构

下列结构体定义在文件threadpool.c中

任务结构体

typedef struct Task
{
    void (*function)(void* arg);//这是一个函数指针和函数指针的参数
    void* arg;//表示可以接受任何类型的参数
}Task;

任务结构体中包含了函数指针和函数指针的参数。后续我们需要定义添加任务的函数,通过该函数实现任务结构体的初始化。

定义线程池结构体

struct ThreadPool
{
    //任务队列
    Task* taskQueue;	//任务队列
    int queueCapacity; //容量
    int queueSize;     //当前任务个数
    int queueFront;    //队头->取数据
    int queueTail;     //队尾->放数据

    // 管理者线程
    pthread_t managerID; //管理者线程ID
    pthread_t *threadIDs; //工作的线程ID
    int minNums;    //最小线程数
    int maxNums;    //最大线程数
    int busyNums;    //忙的线程数
    int liveNums;    //存活的线程
    int exitNums;   // 要杀死的线程个数

    //锁
    pthread_mutex_t mutexPool;  //锁整个的线程池
    pthread_mutex_t mutexBusy;  //锁busyNums,因为其变化比较频繁

    //条件变量
    pthread_cond_t notFull;     //任务队列是不是满了
    pthread_cond_t notEmpty;    //任务队列是不是空了

    int shutdown; //是不是要销毁线程池,销毁为1,不销毁为0
};

组织头文件

我们需要在头文件中写入线程池各个操作的函数原型

#ifndef _THREADPOOL_H
#define _THREADPOOL_H
//创建线程池并初始化

//销毁线程池

//给线程池添加任务

//获取线程池中工作的线程的个数

//获取线程池中活着的线程的个数

#endif //_THREADPOOL_H

创建线程池实例

在本节中,我们的目标是创建一个线程池实例,首先需要在头文件中定义创建线程池的函数原型

函数原型定义

我们先分析一下,初始化一个线程池需要什么参数呢?

首先我们初始化的线程池中有需要维护的最小进程,然后我们需要让线程池知道它的最大进程数是多少,进程池中有一个任务队列来存储任务,构建任务队列我们必须要知道任务队列的尺寸

综上所述:我们需要参数minNumThread, maxNumberThread, queueSize

既然是为了创建一个线程池实例,我们的返回值当然就是该线程池的地址ThreadPool*

#ifndef _THREADPOOL_H
#define _THREADPOOL_H

typedef struct ThreadPool ThreadPool;
//创建线程池
ThreadPool* threadPoolCreate(int min, int max, int queueSize)

#endif //_THREADPOOL_H

线程池创建函数实现

初始化线程池结构体指针

我们首先需要初始化一个ThreadPool* pool出来,因为pool在后续代码中,不仅要初始化其中的各类数据结构,还需要将pool返回给调用者,完成线程池的创建。

ThreadPool *threadPoolCreate(int min, int max, int queueSize)
{   
    //如果初始化操作没问题,就应该返回该线程池的结构体指针
    ThreadPool* pool = (ThreadPool*)malloc(sizeof(ThreadPool));
    if (pool == NULL){
            printf("malloc threadpool fail...\n");
            return NULL;
    }
	...
	return pool;
}

初始化线程池结构体的各类参数

  • 为工作线程分配空间
    由于我们需要通过管理者线程来对工作线程进行增加或者减少的操作,所以我们为工作线程分配堆空间,方便管理。
		pool->threadIDs = (pthread_t*)malloc(sizeof(pthread_t)* max);
        if (pool->threadIDs == NULL){
            printf("malloc threadIDs fail...\n");
            return NULL;
        }
        memset(pool->threadIDs, 0, sizeof(pthread_t)* max);
  • 初始化线程池的各个重要参数
        pool->minNums = minNumThread;
        pool->maxNums = maxNumThread;
        pool->busyNums = 0;
        pool->liveNums = minNumThread; //刚开始和最小个数相等
        pool->exitNums = 0;
  • 初始化锁和条件变量
        if (pthread_mutex_init(&pool->mutexPool, NULL) != 0 ||
            pthread_mutex_init(&pool->mutexBusy, NULL) != 0 ||
            pthread_cond_init(&pool->notEmpty, NULL) != 0||
            pthread_cond_init(&pool->notFull, NULL) != 0){
                printf("mutex or cond failed ...\n");
                return NULL;
        }
  • 初始化任务队列
        //任务队列
        pool->taskQ = (Task*)malloc(sizeof(Task)* queueSize);
        pool->queueCapacity = queueSize;
        pool->queueSize = 0;
        pool->queueFront = 0;
        pool->queueTail = 0;

        pool->shutdown = 0;
  • 创建管理者线程和工作线程

在这里管理者线程和工作线程的回调函数,我们会留到后面章节去实现,首先需要明确这两个回调函数的参数。

还记得管理者线程和工作者线程的任务吗?
手写线程池|C语言版(一)|线程池的定义和运行逻辑

很明显,其中manager需要按策略来添加、销毁进程,需要的参数显然包括我们线程池中的大部分参数,所以我们传入pool;

对于worker,我们需要它能够从任务队列中取任务,任务队列及其相关的数据我们都定义在了struct ThreadPool结构体中,所以我们仍然选择传入pool

        //创建线程
        pthread_create(&pool->managerID, NULL, manager, pool);//管理者线程
        for (int i = 0; i < minNumThread; i++){
            pthread_create(&pool->threadIDs[i], NULL, worker, pool);//工作线程
        }

初始化上述结构后,我们应当返回return pool
然而,我们忽视了一个很大的问题,由于我们malloc了好几块内存用于为threadIDspool分配堆内存,但是一旦分配失败,我们应该释放他们占用的空间,不然他们会成为野指针,可能造成内存泄漏。
但是呢,一个一个为他们free比较麻烦,这里推荐一种结构,来增强代码的健壮性。

do{	//把之前分配内存或者其他操作导致的return NULL统一改写成break
	//在函数块的最后进行后续的处理工作(包括内存释放和返回空指针)
	a = malloc();
	if (false){
		break;
	}
	b = malloc();
	if (false){
		break;
	}
	c = malloc();
	if (false){
		break;
	}
}while(0)
free(a);
free(b);
free(c);
return NULL:

定义线程池的结构C代码

typedef struct Task
{
    void (*function)(void* arg);//这是一个函数指针和函数指针的参数
    void* arg;//表示可以接受任何类型的参数
    //这里函数参数是个范型就是借鉴的pthread_creat()函数的第三个参数
}Task;

struct ThreadPool
{
    Task* taskQ;
    int queueCapacity; //容量
    int queueSize;     //当前任务个数
    int queueFront;    //队头->取数据
    int queueTail;     //队尾->放数据

    pthread_t managerID; //管理者线程ID
    pthread_t *threadIDs; //工作的线程ID
    int minNums;    //最小线程数
    int maxNums;    //最大线程数
    int busyNums;    //忙的线程数
    int liveNums;    //存活的线程
    int exitNums;   // 要杀死的线程个数

    pthread_mutex_t mutexPool;  //锁整个的线程池
    pthread_mutex_t mutexBusy;  //锁busyNums,因为其变化比较频繁

    pthread_cond_t notFull;     //任务队列是不是满了
    pthread_cond_t notEmpty;    //任务队列是不是空了

    int shutdown; //是不是要销毁线程池,销毁为1,不销毁为0
};

创建线程池总体C代码

(共50行不到)

ThreadPool *threadPoolCreate(int min, int max, int queueSize)
{   
    //如果初始化操作没问题,就应该返回该线程池的结构题
    ThreadPool* pool = (ThreadPool*)malloc(sizeof(ThreadPool));

    do{
        if (pool == NULL){
            printf("malloc threadpool fail...\n");
            break;
        }

        pool->threadIDs = (pthread_t*)malloc(sizeof(pthread_t)* max);
        if (pool->threadIDs == NULL){
            printf("malloc threadIDs fail...\n");
            break;
        }
        memset(pool->threadIDs, 0, sizeof(pthread_t)* max);
        pool->minNums = min;
        pool->maxNums = max;
        pool->busyNums = 0;
        pool->liveNums = min; //刚开始和最小个数相等
        pool->exitNums = 0;

        if (pthread_mutex_init(&pool->mutexPool, NULL) != 0 ||
            pthread_mutex_init(&pool->mutexBusy, NULL) != 0 ||
            pthread_cond_init(&pool->notEmpty, NULL) != 0||
            pthread_cond_init(&pool->notFull, NULL) != 0){
                printf("mutex or cond failed ...\n");
                break;
        }

        //任务队列
        pool->taskQ = (Task*)malloc(sizeof(Task)* queueSize);
        pool->queueCapacity = queueSize;
        pool->queueSize = 0;
        pool->queueFront = 0;
        pool->queueTail = 0;

        pool->shutdown = 0;

        //创建线程
        pthread_create(&pool->managerID, NULL, manager, pool);//管理者线程
        for (int i = 0; i < min; i++){
            pthread_create(&pool->threadIDs[i], NULL, worker, pool);//工作线程
        }
        return pool;
        
    }while(0);

    // 释放资源
    if (pool && pool->threadIDs) free(pool->threadIDs);
    if (pool && pool->taskQ) free (pool->taskQ);
    if (pool) free(pool);

    return NULL;
    
}

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

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

相关文章

Shell脚本编写-猜测当前系统是哪个发行版

1、编写脚本 该脚本会确定当前系统中可用的包管理器。同时还以已安装的软件包管理器为指导&#xff0c;猜测当前系统是基于哪个 Linux 发行版。 #!/bin/bash #检查当前系统的可用包管理器&#xff0c;以安装的软件包管理器为指导&#xff0c;猜测当前的系统是基于哪个Linux发行…

顺序表常用操作实现算法

查找操作 插入操作 删除操作 小结 参考附录模拟代码&#xff1a; #include <iostream> const int maxn200; //顺序表 typedef struct{//定义静态类型 int num[maxn];// 装数数组 int len;//记录长度 }sqlist; typedef struct{//定义动态类型 int *num;int len; }sqlist…

XYCTF 2024

本博客仅为记录解题的过程&#xff01; MISC game google识图 XYCTF{Papers Please} 熊博士 XYCTF{liu_ye_mei_you_xiao_jj} 疯狂大杂烩&#xff01;九转功成 在远古时期&#xff0c;修仙过程被分为&#xff1a;炼气、筑基、结丹、元婴、化神、炼虚、合体、大乘、渡劫等九…

MOM是什么?

数字化时代&#xff0c;制造企业纷纷引入信息化系统工具来实现数字化转型升级&#xff0c;你可能对OA、CRM、ERP、MES耳熟能详&#xff0c;说起MOM&#xff0c;你了解吗&#xff1f;今天小编跟你一起认识下它。 MOM是什么&#xff1f; MOM&#xff08;制造运营管理&#xff09…

泰迪智能科技受邀参加2024年粤港澳大湾区产教融合技能人才培养联盟理事会会议

4月24日下午&#xff0c;2024年粤港澳大湾区产教融合技能人才培养联盟&#xff08;以下简称联盟&#xff09;理事会会议在白云区成功举办。 会议由广州市人力资源和社会保障局、广州市发展和改革委员会、广州市教育局、广州市工业和信息化局、广州市总工会等单位指导&#xff…

面经总结(二)(数据库)

数据库常识&#xff1a; 1、数据库系统包含什么&#xff1f; 包含了数据库、数据库管理系统、数据库管理员和应用程序。 数据库&#xff08;DB)&#xff1a;顾名思义是存放数据的仓库&#xff0c;实现数据的持久化。 数据库管理系统&#xff08;DBMS)&#xff1a;类似于操作系…

winrar压缩时排除指定目录排除所有子目录下的目录名称排除所有不需要的目录减小备份体积移除中间目录惊喜

winrar排除指定目录所有指定目录 说明(废话)解决方1. 打开 WinRAR。2. 导航到你要压缩的目录&#xff0c;然后选择该目录中的文件或文件夹。3. 点击“添加”按钮。4. 在弹出的“压缩文件名和参数”窗口中&#xff0c;切换到“文件”标签页。5. 在“文件”标签页中&#xff0c;找…

Topaz Gigapixel AI v7.1.2激活版:智能图像增强与放大

Topaz Gigapixel AI&#xff0c;这款基于人工智能技术的图像处理软件&#xff0c;以其卓越的功能和高效的性能&#xff0c;为图像处理领域注入了新的活力。 Topaz Gigapixel AI v7.1.2激活版下载 作为一款专注于图像增强与放大的软件&#xff0c;Topaz Gigapixel AI利用深度学习…

数据结构11:二叉树的链式结构

文章目录 快速创建链式二叉树二叉树的遍历前序、中序、后序层序 二叉树的基本操作二叉树的节点个数二叉树叶节点的个数二叉树第k层结点个数二叉树查找值为x的结点 二叉树基础oj练习单值二叉树检查两颗树是否相同对称二叉树二叉树的前序遍历另一颗树的子树 二叉树的创建和销毁二…

哈密顿函数和正则方程

9-2 哈密顿函数和正则方程_哔哩哔哩_bilibili 拉格朗日函数是广义坐标和广义速度的函数 哈密顿函数是广义坐标和广义动量的函数 拉格朗日函数经过勒让德变换得到哈密顿函数

设计普遍逼近的深度神经网络:一阶优化方法

论文地址&#xff1a;https://ieeexplore.ieee.org/document/10477580 传统的基于优化的神经网络设计方法通常从一个具有显式表示的目标函数出发&#xff0c;采用特定的优化算法进行求解&#xff0c;再将优化迭代格式映射为神经网络架构&#xff0c;例如著名的 LISTA-NN 就是利…

无人零售与传统便利店的竞争优势

无人零售与传统便利店的竞争优势 成本控制 • 无人零售 显著降低了人力成本&#xff0c;无需支付店员薪资和相关福利&#xff0c;且通过智能化管理减少能源消耗与维护费用&#xff0c;尤其在高租金和高人流区域效益突出。 • 传统便利店 则承担较高的人员开支&#xff0c;…

linux安装maven

linux安装maven 先安装java环境&#xff0c;比如笔者自己的这个 http://t.csdnimg.cn/mNpFO 现在版本已经来到了3.9.6 1、下载这个maven的link链接 2、创建文件夹 mkdir -p /usr/local/maven #为了可以上传成功(也可以不用。) chmod -R 777 /usr/local/maven #这个可以使用…

【深入理解神经网络:预测和评估】

文章目录 前言环境准备数据导入和处理数据归一化神经网络的创建与训练预测与评估结果可视化应用结论 前言 在这篇博客文章中&#xff0c;我们将深入研究利用神经网络进行数据预测和性能评估的过程。我们将详解在MATLAB环境下使用的一个例子&#xff0c;该例子展示了如何使用MAT…

学pyhton的第二十二天

原文链接&#xff1a;Python 图形化界面设计&#xff08;Tkinter&#xff09; - 简书 (jianshu.com) 相关博客链接 接第十八天Tkinter的内容&#xff1a; 单选按钮&#xff08;控件&#xff1a;Radiobutton&#xff09;&#xff1a; 除共有属性外&#xff0c;还具有显示文本…

uniapp对uni.request()的封装以及使用

官方文档 uni.request(OBJECT) | uni-app官网 (dcloud.net.cn) uni.request参数 参数名说明url是写api地址的data是用来传值的对于 GET 方法&#xff0c;会将数据 转换为 query string。例如 { name: name, age: 18 } 转换后的结果是 namename&age18。对于 POST 方法且 …

BUUCTF:Basic 解析(一)

一、Linux Labs 打开靶场 F12 源代码啥也没有&#xff0c;但是题目给出了 ssh 连接的用户名密码端口号及主机&#xff0c;推测应该是要连接&#xff0c;打开 XShell 连接 设置用户名及密码 连接成功&#xff0c;随后找到 flag 二、BUU LFI COURSE 1 打开靶场 F12 检查源代码…

微火快报:全域外卖服务商是什么?如何成为全域外卖服务商?

最近&#xff0c;互联网行业又出现了一个新名词——全域外卖服务商&#xff0c;很多人都预感到它可能是下一个风口&#xff0c;却因为不了解概念和找不到入局的途径而止步不前。那么本期&#xff0c;我们就来重点讲一讲全域外卖服务商的概念、发展前景以及入局的途径。 所谓的全…

电脑进水不用怕,教你几招恢复如初!

电脑进水是一种常见但严重的问题&#xff0c;可能会导致电脑损坏&#xff0c;甚至无法正常使用。在遇到电脑进水的情况下&#xff0c;正确的处理方式至关重要&#xff0c;可以最大程度地减少损害并提高修复成功的可能性。本文将介绍三种解决电脑进水问题的方法&#xff0c;帮助…

flutter笔记-主要控件及布局

文章目录 1. 富文本实例2. Image2.1 本地图片2.2 网络图片 笔记3. 布局4. 滑动相关view4.1 GridView类似九宫格view4.2 ListView 关于widget的生命周期的相关知识这里就不做介绍&#xff0c;和很多语言类似&#xff1b; 1. 富文本实例 Dart中使用richtext&#xff0c;示例如下…
最新文章