C++线程池的实现
线程池,简单来说就是有一堆已经创建好的线程(最大数目一定),初始时他们都处于空闲状态,当有新的任务进来,从线程池中取出一个空闲的线程处理任务,然后当任务处理完成之后,该线程被重新放回到线程池中,供其他的任务使用,当线程池中的线程都在处理任务时,就没有空闲线程供使用,此时,若有新的任务产生,只能等待线程池中有线程结束任务空闲才能执行,下面是线程池的工作原理图:

我们为什么要使用线程池呢?
简单来说就是线程本身存在开销,我们利用多线程来进行任务处理,单线程也不能滥用,无止禁的开新线程会给系统产生大量消耗,而线程本来就是可重用的资源,不需要每次使用时都进行初始化,因此可以采用有限的线程个数处理无限的任务。
废话少说,直接上代码
首先是用条件变量和互斥量封装的一个状态,用于保护线程池的状态
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.c
#include "condition.h" //初始化
int condition_init(condition_t *cond)
{
int status;
if((status = pthread_mutex_init(&cond->pmutex, NULL)))
return status; if((status = pthread_cond_init(&cond->pcond, NULL)))
return status; return ;
} //加锁
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 ;
}
然后是线程池对应的threadpool.h和threadpool.c
#ifndef _THREAD_POOL_H_
#define _THREAD_POOL_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; //线程池中kongxi线程数
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 *arg), void *arg); //摧毁线程池
void threadpool_destroy(threadpool_t *pool); #endif
#include "threadpool.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h> //创建的线程执行
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()
{
timeout = ;
//访问线程池之前需要加锁
condition_lock(&pool->ready);
//空闲
pool->idle++;
//等待队列有任务到来 或者 收到线程池销毁通知
while(pool->first == NULL && !pool->quit)
{
//否则线程阻塞等待
printf("thread %d is waiting\n", (int)pthread_self());
//获取从当前时间,并加上等待时间, 设置进程的超时睡眠时间
clock_gettime(CLOCK_REALTIME, &abstime);
abstime.tv_sec += ;
int status;
status = condition_timedwait(&pool->ready, &abstime); //该函数会解锁,允许其他线程访问,当被唤醒时,加锁
if(status == ETIMEDOUT)
{
printf("thread %d wait timed out\n", (int)pthread_self());
timeout = ;
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--;//当前工作的线程数-1
//若线程池中没有线程,通知等待线程(主线程)全部任务已经完成
if(pool->counter == )
{
condition_signal(&pool->ready);
}
condition_unlock(&pool->ready);
break;
}
//超时,跳出销毁线程
if(timeout == )
{
pool->counter--;//当前工作的线程数-1
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)
{ condition_init(&pool->ready);
pool->first = NULL;
pool->last =NULL;
pool->counter =;
pool->idle =;
pool->max_threads = threads;
pool->quit =; } //增加一个任务到线程池
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 > )
{
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);
//设置销毁标记为1
pool->quit = ;
//线程池中线程个数大于0
if(pool->counter > )
{
//对于等待的线程,发送信号唤醒
if(pool->idle > )
{
condition_broadcast(&pool->ready);
}
//正在执行任务的线程,等待他们结束任务
while(pool->counter)
{
condition_wait(&pool->ready);
}
}
condition_unlock(&pool->ready);
condition_destroy(&pool->ready);
}
测试代码:
#include "threadpool.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h> void* mytask(void *arg)
{
printf("thread %d is working on task %d\n", (int)pthread_self(), *(int*)arg);
sleep();
free(arg);
return NULL;
} //测试代码
int main(void)
{
threadpool_t pool;
//初始化线程池,最多三个线程
threadpool_init(&pool, );
int i;
//创建十个任务
for(i=; i < ; i++)
{
int *arg = malloc(sizeof(int));
*arg = i;
threadpool_add_task(&pool, mytask, arg); }
threadpool_destroy(&pool);
return ;
}
输出结果:

可以看出程序先后创建了三个线程进行工作,当没有任务空闲时,等待2s直接退出销毁线程
C++线程池的实现的更多相关文章
- 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...
- C#多线程之线程池篇3
在上一篇C#多线程之线程池篇2中,我们主要学习了线程池和并行度以及如何实现取消选项的相关知识.在这一篇中,我们主要学习如何使用等待句柄和超时.使用计时器和使用BackgroundWorker组件的相关 ...
- C#多线程之线程池篇2
在上一篇C#多线程之线程池篇1中,我们主要学习了如何在线程池中调用委托以及如何在线程池中执行异步操作,在这篇中,我们将学习线程池和并行度.实现取消选项的相关知识. 三.线程池和并行度 在这一小节中,我 ...
- C#多线程之线程池篇1
在C#多线程之线程池篇中,我们将学习多线程访问共享资源的一些通用的技术,我们将学习到以下知识点: 在线程池中调用委托 在线程池中执行异步操作 线程池和并行度 实现取消选项 使用等待句柄和超时 使用计时 ...
- NGINX引入线程池 性能提升9倍
1. 引言 正如我们所知,NGINX采用了异步.事件驱动的方法来处理连接.这种处理方式无需(像使用传统架构的服务器一样)为每个请求创建额外的专用进程或者线程,而是在一个工作进程中处理多个连接和请求.为 ...
- Java线程池解析
Java的一大优势是能完成多线程任务,对线程的封装和调度非常好,那么它又是如何实现的呢? jdk的包下和线程相关类的类图. 从上面可以看出Java的线程池主的实现类主要有两个类ThreadPoolEx ...
- Android线程管理之ExecutorService线程池
前言: 上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Execu ...
- Android线程管理之ThreadPoolExecutor自定义线程池
前言: 上篇主要介绍了使用线程池的好处以及ExecutorService接口,然后学习了通过Executors工厂类生成满足不同需求的简单线程池,但是有时候我们需要相对复杂的线程池的时候就需要我们自己 ...
- -Android -线程池 批量上传图片 -附php接收代码
(出处:http://www.cnblogs.com/linguanh/) 目录: 1,前序 2,类特点 3,用法 4,java代码 5,php代码 1,前序 还是源于重构,看着之前为赶时间写着的碎片 ...
- C#多线程--线程池(ThreadPool)
先引入一下线程池的概念: 百度百科:线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行, ...
随机推荐
- Windows安装使用SonarQube7.4 对java项目进行代码质量扫描
我这里使用7.4因为使用JDK是1.8 其它版本看下依赖版本就好 1.下载7.4版本安装包 https://binaries.sonarsource.com/CommercialDistributio ...
- java笔记——手写+键入
Frame.setDefaultCloseOpreation() Default:默认的 设置默认关闭操作 (0:什么都不干: 1:隐藏窗口: 2:关闭窗口但继续运行程序: 3:关闭窗口关闭程序:) ...
- Html4.0.1 标签使用笔记
1.lang=zh,en有什么用? 告诉搜索引擎爬虫,网站是关于什么内容的,优先显示网站排名.一般竞价排名,根据百度搜索引擎,需要签订关键词协议,首页第一个竞价排名大概是30-50元/条,竞价排名范围 ...
- 注意力机制和Seq2seq模型
注意力机制 在"编码器-解码器(seq2seq)"⼀节⾥,解码器在各个时间步依赖相同的背景变量(context vector)来获取输⼊序列信息.当编码器为循环神经⽹络时,背景变量 ...
- 2019牛客网暑假多校训练第四场 K —number
链接:https://ac.nowcoder.com/acm/contest/884/K来源:牛客网 题目描述 300iq loves numbers who are multiple of 300. ...
- JS 日期格式化为 2020-11-01 22:33:44 格式
项目中经常会用到将JS日期格式化输出为 标准格式(2020-11-01 22:33:44)的问题.写个精简版的,代码如下: function formatDate(d){ d = d || new D ...
- 时间复杂度T(n)
1:概念 T(n)被称为时间复杂度,一般为在某个算法中操作步骤的重复次数与问题规模n的关系,下面一一举例说明 2:具体说明 2.1:常数阶o(1) 无论代码有多少行,只要没有循环等复杂的结构,其算法时 ...
- 记一次asp.net core 线上崩溃解决总结
1.首先要先准备好环境,安装lldb 工具 要安装3.9版本的,因为每个版本对应dnc版本不一样,3.9的支持2.2 版本,然后确定分析的机器里dnc 版本和线上的生产环境是否一致,自己安装比较费劲, ...
- maven deploy到私服去掉时间戳
直接上代码在插件中的配置. <plugin> <artifactId>maven-war-plugin</artifactId> <version>3. ...
- PAT Advanced 1070 Mooncake (25) [贪⼼算法]
题目 Mooncake is a Chinese bakery product traditionally eaten during the Mid-Autumn Festival. Many typ ...