手写线程池|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; //管理者线程IDpthread_t *threadIDs; //工作的线程IDint 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_Htypedef 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; //管理者线程IDpthread_t *threadIDs; //工作的线程IDint 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.mzph.cn/pingmian/5633.shtml

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

相关文章

TCP/IP和HTTP协议

TCP/IP OSI 七层模型在提出时的出发点是基于标准化的考虑&#xff0c;而没有考虑到具体的市场需求&#xff0c;使得该模型结构复杂&#xff0c;部分功能冗余&#xff0c;因而完全实现 OSI 参考模型的系统不多。而 TCP/IP 参考模型直接面向市场需求&#xff0c;实现起来也比较…

一文掌握:Clipboard API ,让前端顺畅操作剪贴板,无论怎么复制,都显示你网站信息。

一、Clipboard API是什么 Clipboard API 是一个 Web API&#xff0c;它提供了一种在网页上读取和写入剪贴板内容的方式。通过 Clipboard API&#xff0c;网页可以访问用户的剪贴板&#xff0c;从中读取文本、图像或其他数据&#xff0c;并且可以将数据写入剪贴板&#xff0c;以…

FFmpeg基础知识详解:音频视频处理的强大工具

FFmpeg&#xff0c;这个强大的开源多媒体框架&#xff0c;已经成为全球范围内音频、视频处理和流媒体传输领域的基石。它集合了音频解码、编码、转码、混合、抓取、流化等多种功能于一身&#xff0c;几乎能满足任何与音视频处理相关的技术需求。本文将带你走进FFmpeg&#xff0…

2024五一杯C题思路代码文章成品分享:煤矿深部开采冲击地压危险预测

提供的数据分为五类&#xff1a; A:正常工作数据 B:前兆特征数据 C:干扰信号数据 D:传感器断线数据 E:工作面休息数据 任务细节&#xff1a; 任务1&#xff1a; 分析含干扰的电磁辐射和声发射信号&#xff0c;识别干扰信号的特征&#xff0c;并利用这些特征在特定时间段…

安装Kuboard管理k8s

一、Kuboard 介绍 Kuboard 是一款免费的 Kubernetes 管理工具,提供了丰富的功能,结合已有或新建的代码仓库、镜像仓库、CI/CD工具等,可以便捷的搭建一个生产可用的 Kubernetes 容器云平台,轻松管理和运行云原生应用。您也可以直接将 Kuboard 安装到现有的 Kubernetes 集群…

计算机毕业设计python_django宠物领养系统z6rfy

本宠物领养系统主要包括两大功能模块&#xff0c;即管理员模块、用户模块。下面将对这两个大功能进行具体功能需求分析。 &#xff08;1&#xff09;管理员&#xff1a;管理员登录后主要功能包括个人中心、用户管理、送养宠物管理、地区类型管理、失信黑名单管理、申请领养管理…

深入解析Jackson的ObjectMapper:核心功能与方法指南

com.fasterxml.jackson.databind.ObjectMapper 是Jackson库的核心类&#xff0c;负责JSON序列化与反序列化的重任。本文旨在详细介绍其成员属性和方法&#xff0c;帮助开发者更好地利用Jackson进行Java对象与JSON数据之间的转换操作。 初始化与配置 构造与复制 默认构造函数…

封装umi-request时通过 AbortController 配置取消请求

一、关键部分 一、在封装的request.ts中 声明一个 abortControllers 对象用于存储要取消的请求&#xff08;我用了-s表示复数&#xff0c;多个abortcontroller对象&#xff0c;与下面&#x1f447;的单个abortController区分&#xff09;封装取消请求的函数cancelRequest, 传入…

038——基于STM32和I.MX6uLL实现uart控制GPS(失败者总结)

目录 1、GPS模块简介 2、GPS数据格式 3、方案梳理 1、GPS模块简介 全球定位系统(Global Positioning System&#xff0c; GPS)是一种以空中卫星为基础的高精度无线电导航的定位系统&#xff0c;它在全球任何地方以及近地空间都能够提供准确的地理位置、车行速度及精确的时间…

邦注科技 温控箱对企业的重要性

注塑加工是将加热的熔融塑料注入模具中形成所需产品的工艺过程。良好的注塑加工工艺需要控制好许多参数&#xff0c;其中最重要的因素之一就是模具的温度。模具温度的不稳定会导致产品尺寸大小、表面缺陷等方面的问题&#xff0c;甚至会导致生产不良品&#xff0c;加大生产成本…

【webrtc】MessageHandler 5: 基于线程的消息处理:以PeerConnection信令线程为例

peerconn的信令是通过post 消息到自己的信令线程消息来处理的PeerConnectionMessageHandler 是具体的处理器G:\CDN\rtcCli\m98\src\pc\peer_connection_message_handler.hMachinery for handling messages posted to oneself PeerConnectionMessageHandler 明确服务于 signalin…

2021江苏省赛 H-Reverse the String

来源 题目 There is a string of lowercase letters, and you want to minimize its lexicographical order. What you can do is reverse an interval or do nothing. For example, for the string abcdefg, if we reverse the interval abcdefg, it will become abfedcg. A …

017、Python+fastapi,第一个Python项目走向第17步:ubuntu24.04 无界面服务器版下安装nvidia显卡驱动

一、说明 新的ubuntu24.04正式版发布了&#xff0c;前段时间玩了下桌面版&#xff0c;感觉还行&#xff0c;先安装一个服务器无界面版本吧 安装时有一个openssh选择安装&#xff0c;要不然就不能ssh远程&#xff0c;我就是没选&#xff0c;后来重新安装ssh。 另外一个就是安…

数据仓库和数据仓库分层

一、数据仓库概念 数据仓库(Data Warehouse)&#xff0c;可简写为DW或DWH。数据仓库&#xff0c;是为企业所有级别的决策制定过程&#xff0c;提供所有类型数据支持的战略集合。它是单个数据存储&#xff0c;出于分析性报告和决策支持目的而创建。 为需要业务智能的企业&#…

CGAL 点云数据生成DSM、DTM、等高线和数据分类

原文链接 CGAL 点云数据生成DSM、DTM、等高线和数据分类 - 知乎 在GIS应用软件中使用的许多传感器(如激光雷达)都会产生密集的点云。这类应用软件通常利用更高级的数据结构&#xff1a;如&#xff1a;不规则三角格网 (TIN)是生成数字高程模型 (DEM) 的基础&#xff0c;也可以利…

2024深圳杯数学建模竞赛A题(东三省数学建模竞赛A题):建立火箭残骸音爆多源定位模型

更新完整代码和成品完整论文 《2024深圳杯&东三省数学建模思路代码成品论文》↓↓↓&#xff08;浏览器打开&#xff09; https://www.yuque.com/u42168770/qv6z0d/zx70edxvbv7rheu7?singleDoc# 2024深圳杯数学建模竞赛A题&#xff08;东三省数学建模竞赛A题&#xff0…

PyVista 3D数据可视化 Python 库 简介 含源码

Pyvista是一个用于科学可视化和分析的Python库 &#xff1b;我认为它适合做一些网格数据的处理&#xff1b; 它封装了VTK&#xff08;Visualization Toolkit&#xff09;之上&#xff0c;提供了一些高级接口&#xff0c; 3D数据可视化变得更加简单和易用。 1.安装 pyvista&…

开发一个语音聊天社交app小程序H5需要多少钱?

社交&#xff0c;即时通讯APP系统。如何开发一个社交App||开发一个即时通信应用是一项复杂而充满挑战的任务&#xff0c;需要考虑多个技术、开发时间和功能方面的因素。以下是一个概要&#xff0c;描述了从技术、开发时间和功能角度如何开发这样的应用&#xff1a; 1. 技术要点…

12、Flink 的 Keyed State 代码示例

1、KeyedState 用例 import org.apache.flink.api.common.functions.AggregateFunction; import org.apache.flink.api.common.functions.ReduceFunction; import org.apache.flink.api.common.state.*; import org.apache.flink.api.common.typeinfo.TypeHint; import org.ap…

70.网络游戏逆向分析与漏洞攻防-角色与怪物信息的更新-整理与角色数据更新有关的数据

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 现在的代码都是依据数据包来写的&#xff0c;如果看不懂代码&#xff0c;就说明没看懂数据包…