【C++】5.多线程:ThreadPoll线程池实现

news2024/11/24 15:45:45

😏★,°:.☆( ̄▽ ̄)/$:.°★ 😏
这篇文章主要介绍ThreadPoll线程池实现。
学其所用,用其所学。——梁启超
欢迎来到我的博客,一起学习,共同进步。
喜欢的朋友可以关注一下,下次更新不迷路🥞

文章目录

    • :smirk:1. 线程池介绍
    • :blush:2. 线程池实现方法
    • :satisfied:3. 线程池实现示例

😏1. 线程池介绍

线程池是一种线程管理的抽象概念,它主要用于优化多线程应用程序的性能和资源利用。在多线程编程中,创建和销毁线程是一个开销较大的操作。线程池通过预先创建一组线程,并将任务提交给这些线程来执行,从而避免了重复创建和销毁线程的开销。

线程池通常由以下几个组件组成:

1.任务队列(Task Queue):用于存储待执行的任务。当任务提交到线程池时,它们被放置在任务队列中等待执行。

2.线程池管理器(Thread Pool Manager):负责创建、管理和调度线程池中的线程。它控制着线程的数量,可以动态地增加或减少线程的数量,以适应不同的工作负载。

3.工作线程(Worker Threads):线程池中的实际执行单元。它们不断地从任务队列中获取任务并执行。

4.任务接口(Task Interface):定义了要执行的任务的接口。通常,任务是以函数或可运行对象的形式表示。

使用线程池的好处包括:

提高性能:线程池可以减少线程的创建和销毁次数,避免了频繁的上下文切换,提高了多线程程序的性能和响应速度。

资源管理:线程池可以限制并发线程的数量,避免资源过度占用,从而更好地管理系统资源。

提高可扩展性:通过调整线程池的大小,可以适应不同的并发需求,提高系统的可扩展性。

总之,线程池是一种重要的多线程编程技术,它能够有效地管理和利用线程,提高程序的性能和资源利用率。

😊2. 线程池实现方法

如上所述,实现线程池主要有4个部分:

  1. 线程管理器:用于创建并管理线程池。
  2. 工作线程:线程池中实际执行任务的线程。在初始化线程时会预先创建好固定数目的线程在池中,这些初始化的线程一般处于空闲状态。
  3. 任务接口:每个任务必须实现的接口。当线程池的任务队列中有可执行任务时,被空间的工作线程调去执行(线程的闲与忙的状态是通过互斥量实现的),把任务抽象出来形成一个接口,可以做到线程池与具体的任务无关。
  4. 任务队列:用来存放没有处理的任务。提供一种缓冲机制。实现这种结构有很多方法,常用的有队列和链表结构。

😆3. 线程池实现示例

参考Github:https://github.com/volute24/ThreadPoll

// main.cpp
#include "threadpool.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define THREADPOOL_MAX_NUM 5

void* mytask(void *arg)
{
    printf("thread %d is working on task %d\n", (int)pthread_self(), *(int*)arg);
    sleep(1);
    free(arg);
    return NULL;
}

int main(int argc, char* argv[])
{
    threadpool_t pool;
    // 初始化线程池,最多5个线程
    threadpool_init(&pool, THREADPOOL_MAX_NUM);
	// 创建10个任务
    for(int i=0; i < 10; i++)
    {
        int *arg =(int *)malloc(sizeof(int));
        *arg = i;
        threadpool_add_task(&pool, mytask, arg);
        //printf("arg address:%p,arg:[%d],i:[%d]\n",arg,*arg,i);
    }
    threadpool_destroy(&pool);
    return 0;
}
// threadpool.h
#ifndef _THREAD_POLL_H_
#define _THREAD_POLL_H_

#include "condition.h"

// 封装线程池中的对象需要执行的任务对象
typedef struct task
{
	void *(*run)(void *args);//函数指针,需要执行的任务
	void *arg;			//参数
	struct task *next;	//任务队列中下一个任务
}task_t;

//定义线程池结构体
typedef struct threadpool
{
	condition_t ready;   //状态量
	task_t *first;	     //任务队列中第一个任务
	task_t *last;		//任务队列中最后一个任务
	int counter;		//线程池中已有线程数
	int idle;			//线程池中空闲线程数
	int max_threads;	//线程池最大线程数
	int quit;			//是否退出标志
}threadpool_t;

//线程池初始化
void threadpool_init(threadpool_t *pool,int threads);
//线程池中加入任务
void threadpool_add_task(threadpool_t *pool,void *(*run)(void *args),void *arg);
//销毁线程池
void threadpool_destroy(threadpool_t *pool);

#endif
// threadpoll.cpp
#include "threadpool.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>

using namespace std;

//线程执行
void *thread_routine(void *arg)
{
	struct timespec abstime;
	int timeout;
	printf("thread %d is starting\n",(int)pthread_self());
	threadpool_t *pool = (threadpool_t *)arg;

	while(1)
	{
		timeout = 0;
		//访问池前加锁
		condition_lock(&pool->ready);
		//空闲
		pool->idle++;
		//等待队列任务|| 收到线程池销毁通知
		while(pool->first == NULL && !pool->quit)
		{
			//否则线程阻塞等待
			printf("thread %d is waiting\n",(int)pthread_self());
			//获取从当前时间加上等待时间,设置超时睡眠时间
			//clock_gettime 在编译链接时需加上 -lrt ,librt中实现了clock_gettime函数
			clock_gettime(CLOCK_REALTIME,&abstime);  //CLOCK_REALTIME 系统实时时间
			abstime.tv_sec += 2;
			int status;
			status = condition_timedwait(&pool->ready,&abstime);
			if(status == ETIMEDOUT)
			{
				printf("thread %d wait timed out\n",(int)pthread_self());
				timeout = -1;
				break;
			}
		}

		pool->idle--;
		
		if(pool->first != NULL)
		{
		 	//取出等待队列最前任务,移除任务,并执行任务
		 	task_t *t = pool->first;
			pool->first = t->next;
			//由于任务执行需要消耗时间,先解锁让其他线程访问线程池
			condition_unlock(&pool->ready);
			//执行任务
			t->run(t->arg);
			//执行完任务释放内存
            free(t);
           	//重新加锁
            condition_lock(&pool->ready);	
		}
		//退出线程池
	        if(pool->quit && pool->first == NULL)
	        {
	            pool->counter--;
	            //若线程池中没有线程,通知等待线程(主线程)全部任务已经完成
	            if(pool->counter == 0)
	            {
	                condition_signal(&pool->ready);
	            }
	            condition_unlock(&pool->ready);
	            break;
	        }
		 	//超时,跳出销毁线程
	        if(timeout == 1)
	        {
	            pool->counter--;
	            condition_unlock(&pool->ready);
	            break;
	        }
			condition_unlock(&pool->ready);
	}
    printf("thread %d is exiting\n", (int)pthread_self());
    return NULL;
}

//线程池初始化
void threadpool_init(threadpool_t *pool, int threads)
{
    
    int nstatu = condition_init(&pool-ready>);
    printf("Init return values:%d\n",nstatu);
    pool->first = NULL;
    pool->last =NULL;
    pool->counter =0;
    pool->idle =0;
    pool->max_threads = threads;
    pool->quit =0;
    
}

//增加一个任务到线程池
void threadpool_add_task(threadpool_t *pool, void *(*run)(void *arg), void *arg)
{
    //产生一个新的任务
    task_t *newtask = (task_t *)malloc(sizeof(task_t));
    newtask->run = run;
    newtask->arg = arg;
    newtask->next=NULL;//新加的任务放在队列尾端
    
    //线程池的状态被多个线程共享,操作前需要加锁
    condition_lock(&pool->ready);
    
    if(pool->first == NULL)
    {
        pool->first = newtask;
    }        
    else    
    {
        pool->last->next = newtask;
    }
    pool->last = newtask;  //队列尾指向新加入的线程
    
    //线程池中有线程空闲,唤醒
    if(pool->idle > 0)
    {
        condition_signal(&pool->ready);
    }
    //当前线程池中线程个数没有达到设定的最大值,创建一个新的线程
    else if(pool->counter < pool->max_threads)
    {
        pthread_t tid;
        pthread_create(&tid, NULL, thread_routine,pool);
        pool->counter++;
    }
    //结束,访问解锁
    condition_unlock(&pool->ready);
}

//线程池销毁
void threadpool_destroy(threadpool_t *pool)
{
    if(pool->quit)
    {
    return;
    }
    condition_lock(&pool->ready);
    pool->quit = 1;
    //线程池中线程个数大于0
    if(pool->counter > 0)
    {
        //对于等待的线程,发送信号唤醒
        if(pool->idle > 0)
        {
            condition_broadcast(&pool->ready);
        }
        //正在执行任务的线程,等待他们结束任务
        while(pool->counter)
        {
            condition_wait(&pool->ready);
        }
    }
    condition_unlock(&pool->ready);
    condition_destroy(&pool->ready);
}

//condition.h
#ifndef _CONDITION_H_
#define _CONDITION_H_

#include <pthread.h>

//封装互斥量和条件变量作为初始状态
typedef struct condition
{
	pthread_mutex_t pmutex;
	pthread_cond_t  pcond;
}condition_t;

//对状态操作函数
int condition_init(condition_t *cond);
int condition_lock(condition_t *cond);
int condition_unlock(condition_t *cond);
//条件等待
int condition_wait(condition_t *cond);
//计时等待
int condition_timedwait(condition_t *cond,const struct timespec *abstime);
//激活一个等待该条件的线程
int condition_signal(condition_t *cond);
//激活所有等待线程
int condition_broadcast(condition_t *cond);
int condition_destroy(condition_t *cond);

#endif
// condition.cpp
#include "condition.h"
#include "stdio.h"
//初始化
int condition_init(condition_t *cond)
{
	int status;
	if((status = pthread_mutex_init(&cond->pmutex,NULL)))
	{
		printf("pthread_mutex_init return value:%d\n",status);
		return status;
	}
	
	if((status = pthread_cond_init(&cond->pcond,NULL)))
	{
		printf("pthread_cond_init  return value:%d\n",status);
		return status;
	}
		
	return 0;
}

//加锁
int condition_lock(condition_t *cond)
{
	return pthread_mutex_lock(&cond->pmutex);
}

//解锁
int condition_unlock(condition_t *cond)
{
    return pthread_mutex_unlock(&cond->pmutex);
}

//等待
int condition_wait(condition_t * cond)
{
	return pthread_cond_wait(&cond->pcond,&cond->pmutex);
}

//固定时间等待
int condition_timedwait(condition_t *cond, const struct timespec *abstime)
{
	return pthread_cond_timedwait(&cond->pcond,&cond->pmutex,abstime);
}

//唤醒一个睡眠线程
int condition_signal(condition_t *cond)
{
	return pthread_cond_signal(&cond->pcond);
}

//唤醒所有睡眠线程
int condition_broadcast(condition_t * cond)
{
	return pthread_cond_broadcast(&cond->pcond);
}

//销毁锁
int condition_destroy(condition_t * cond)
{
	int status;
	if((status = pthread_mutex_destroy(&cond->pmutex)))
	{
		return status;
	}
	if((status = pthread_cond_destroy(&cond->pcond)))
	{
		return status;
	}
	return 0;
}

编译:g++ main.cpp condition.cpp threadpool.cpp -lpthread

运行如下:

Init return values:0
thread 1696954112 is starting
thread 1705346816 is starting
thread 1688561408 is starting
thread 1688561408 is working on task 0
thread 1671776000 is starting
thread 1671776000 is working on task 2
thread 1705346816 is working on task 1
thread 1696954112 is working on task 3
thread 1680168704 is starting
thread 1680168704 is working on task 4
thread 1688561408 is working on task 5
thread 1671776000 is working on task 6
thread 1705346816 is working on task 7
thread 1680168704 is working on task 8
thread 1696954112 is working on task 9
thread 1680168704 is exiting
thread 1705346816 is exiting
thread 1696954112 is exiting

在这里插入图片描述

以上。

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

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

相关文章

Web3.0 在中国市场的规模如何?其特点有什么?

随着区块链技术的不断发展和普及&#xff0c;Web3.0 作为下一代互联网的发展趋势&#xff0c;在中国市场也逐渐受到了关注和应用。那么&#xff0c;Web3.0 在中国市场的规模如何&#xff1f;其特点又有哪些呢&#xff1f; 首先&#xff0c;让我们来看一下 Web3.0 在中国市场的规…

Python3,Pandas这4种高频使用的筛选数据的方法,不得不说,确实挺好。

Pandas数据筛选方法 1、引言2、4种高频使用数据筛选方法2.1 布尔索引2.2 isin()方法2.3 query()方法2.4 loc[]方法 3、总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c;share一下 数据筛选的方法呗 小鱼&#xff1a;Excel就可以啊 小屌丝&#xff1a;我要用Pandas 小鱼&#…

0073. 矩阵置零

73. 矩阵置零 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;解法一&#xff1a;构造一个同等规模的二维数组&#xff0c;即所谓的m*n解法二&#xff1a; int row[] new int[m]; int col[] new int[n];解法三&#xff1a;常数量级 参考代码&#xff1a; 原题…

软件测试技能,JMeter压力测试教程,请求头部自动签名带上X-sign参数(二十二)

一、前言 接口请求 body 带有 sign 签名参数&#xff0c;sign 签名是根据请求 body 除去 sign 本身参数后&#xff0c;拼接请求参数最后 md5 加密生成的 前面一篇是把 sign 前面参数放到请求的 body 里面&#xff0c;这篇继续讲把签名参数放到请求头部的情况 二、实现方式 …

3Ds Max坐标轴切换,使用物体的世界坐标和本地坐标之间切换

标题&#xff1a;当挪动物体的时候想使用&#xff08;本地&#xff09;/&#xff08;世界&#xff09;坐标移动 官方文档 https://help.autodesk.com/view/3DSMAX/2023/CHS/?guidGUID-0F3E2822-9296-42E5-A572-B600884B07E3官方文档 使用“参考坐标系”列表&#xff0c;可以…

怎么把音乐的伴奏提取出来?分享几个方法给大家!

歌曲伴奏提取是一种将歌曲中的人声去除&#xff0c;获得只含伴奏的音乐文件的方法。这项技术可以广泛应用于伴唱、演奏、混音等领域。以下将详细介绍四种常用的歌曲伴奏提取方法&#xff0c;并提供记灵在线工具的使用说明&#xff0c;让您能轻松进行伴奏提取。 一、使用记灵在线…

Linux下载不同版本的gcc与g++并编译,演示安装gcc11

1. 引言 系统: ubuntu 1804点我进入清华源-GCC链接: https://mirror.tuna.tsinghua.edu.cn/gnu/gcc/ 2. 下载编译指定版本gcc 2.1 下载一个低版本g 为了保证后续安装gcc能编译通过&#xff0c;且安装g同时也会安装gcc&#xff01; sudo apt-get install g2.2 下载指定gcc…

KD06丨超级趋势线第4版大升级

大家好&#xff0c;今天我们来分享可达鸭策略最后一期——超级趋势线第4版&#xff0c;进出场自适应大升级。 从2021年开始&#xff0c;我开始分享超级趋势线系列策略。在最初超级趋势线主体构造不断改造&#xff0c;到加入过滤&#xff0c;到出场迭代等等&#xff0c;历经大版…

Vue中的el-date-picker时间选择器的使用

1、value-format属性设置需要什么格式的时间 2、type类型选择datetime、date 年月日时分秒 <el-date-pickervalue-format"yyyy-MM-dd HH:mm:ss"v-model"excelRuleForm.startTime"type"datetime":placeholder"选择开始时间"> &…

统信UOS系统开发笔记(八):在统信UOS上编译搭建mqtt基础环境(版本使用QMQTT::Client)

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/131455493 红胖子(红模仿)的博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软…

android实现hook其它应用代码和方法调用(无root)

上次讲过无root实现复制其它应用data内部数据&#xff0c;这次讲一下无root情况下直接访问目标应用进程&#xff0c;然后对其进行hook操作&#xff0c;这种跨进程hook的原理是通过dex注入和资源文件修改来实现的。 首先在android studio中创建一个模块&#xff0c;在模块中创建…

ROS学习笔记(实践三)--常见相机问题整理

目录 1.普通的usb摄像头使用 1.普通的usb摄像头使用 安装ros功能包 sudo apt-get install ros-kinetic-usb-cam //注意ros版本 //启动roslaunch usb_cam usb_cam-test.launch启动后弹出如下窗口&#xff1a; 需要修相机驱动时&#xff0c;可以使用源码安装&#xff0c;源码…

基于JavaWeb的网络不良信息举报平台的设计与实现

1.引言 随着互联网的快速发展&#xff0c;网络不良信息问题日益突出&#xff0c;给人们的生活和社会秩序带来了严重的困扰。网络不良信息包括色情、暴力、赌博、诈骗等不良内容&#xff0c;这些信息的传播对青少年的身心健康产生不良影响&#xff0c;也破坏了社会的良好秩序和…

在 7 月 4 日,PoseiSwap 治理通证 $POSE 上线了 BNB Chain 上的头部

在 7 月 4 日&#xff0c;PoseiSwap 治理通证 $POSE 上线了 BNB Chain 上的头部 DEX PancakeSwap&#xff08;POSE/ZBC 交易对&#xff09;&#xff0c;在 $POSE 开盘交易的 10 分钟内&#xff0c;其最高涨幅达到了 2169.22%&#xff0c;所有的早期投资者基本都从中获得了不菲的…

request请求获取参数的实现方法(post和get两种方式)

request请求获取参数的实现方法(post和get两种方式) Servlet代码&#xff1a; package request请求获取Post或者get参数; import java.io.IOException; import java.util.Arrays; import java.util.Enumeration; import java.util.Iterator; import java.util.Map; impo…

Halcon印字缺陷检测

印字缺陷检测 在半导体行业&#xff0c;印字缺陷检测占了很大比例。打印标签上字符的缺失、字符的脏污等印字不良都需要检出。基于匹配和印字区域作差的检测算法思路如下&#xff1a; #mermaid-svg-bmN5WE1wE1PqpI51 {font-family:"trebuchet ms",verdana,arial,san…

论文笔记:Traffic Flow Prediction via Spatial Temporal Graph Neural Network

WWW 2020 1 模型 图神经网络图注意力——空间依赖关系 RNNTransformer——短期&长期依赖关系 缺点&#xff1a;运用RNN于较长序列仍然会带来误差积累&#xff0c;并且RNN模型的运算效率并不高 2 实验

IDEA全版本MyBatisCodeHelper Pro免费使用教程(全部版本适用)

版本说明 系统&#xff1a;macOS&#xff08;Windows同理&#xff09;IDE&#xff1a;IntelliJ IDEA 2023.1.3 &#xff08;哪个版本都可以&#xff09;MyBatisCodeHelper Pro 3.2.1&#xff08;哪个版本都可以&#xff09; 环境准备 从插件市场下载MyBatisCodeHelper Pro 领…

FPGA实验四:交通灯控制器设计

目录 一、实验目的 二、设计要求 三、实验代码 1.design source文件代码 2.仿真文件代码 3.代码原理分析 四、实验结果及分析 1、引脚锁定 2、仿真波形及分析 3、下载测试结果及分析 五、实验心得 1.解决实验中遇见的问题及解决 2.实验完成的心得 一、实验目的 &a…

如何将TXT转换为PDF格式?两种简便方法分享!

在日常办公和学习中&#xff0c;我们常常遇到需要将文本文件转换为PDF格式的需求。TXT是一种常见的文本文件格式&#xff0c;而PDF则是一种更为通用和便捷的文档格式。本文将为大家介绍两种简单易行的方法&#xff0c;帮助您将TXT文件快速转换为PDF格式。 方法一&#xff1a;记…