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 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;
}
复制代码

然后是线程池对应的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(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(CLOCK_REALTIME, &abstime);  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--;//当前工作的线程数-1//若线程池中没有线程,通知等待线程(主线程)全部任务已经完成if(pool->counter == 0){condition_signal(&pool->ready);}condition_unlock(&pool->ready);break;}//超时,跳出销毁线程if(timeout == 1){pool->counter--;//当前工作的线程数-1condition_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 =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);//设置销毁标记为1pool->quit = 1;//线程池中线程个数大于0if(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);
}
复制代码

测试代码:

复制代码
#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(1);free(arg);return NULL;
}//测试代码
int main(void)
{threadpool_t pool;//初始化线程池,最多三个线程threadpool_init(&pool, 3);int i;//创建十个任务for(i=0; i < 10; i++){int *arg = malloc(sizeof(int));*arg = i;threadpool_add_task(&pool, mytask, arg);}threadpool_destroy(&pool);return 0;
}
复制代码

输出结果:

可以看出程序先后创建了三个线程进行工作,当没有任务空闲时,等待2s直接退出销毁线程

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

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

相关文章

Linux 打印可变参数日志

实现了传输进去的字符串所在的文档&#xff0c;函数和行数显示功能。 实现了将传入的可变参数打印到日志功能。 #include<stdio.h> #include<stdarg.h> #include<string.h>const char * g_path "/home/exbot/wangqinghe/log.txt"; #define LOG(fm…

C++强化之路之线程池开发整体框架(二)

一.线程池开发框架 我所开发的线程池由以下几部分组成&#xff1a; 1.工作中的线程。也就是线程池中的线程&#xff0c;主要是执行分发来的task。 2.管理线程池的监督线程。这个线程的创建独立于线程池的创建&#xff0c;按照既定的管理方法进行管理线程池中的所有线程&#xf…

vfprintf()函数

函数声明&#xff1a;int vfprintf(FILE *stream, const char *format, va_list arg) 函数参数&#xff1a; stream—这是指向了FILE对象的指针&#xff0c;该FILE对象标识了流。 format—c语言字符串&#xff0c;包含了要被写入到流stream中的文本。它可以包含嵌入的format标签…

Makefile(二)

将生产的.o文件放进指定的文件中&#xff08;先创建该文件夹&#xff09; src $(wildcard ./*.cpp) obj $(patsubst %.cpp,./output/%.o,$(src))target test$(target) : $(obj)g $(obj) -o $(target) %.o: %.cppg -c $< -o output/$.PHONY:clean clean:rm -f $(target) $…

TCP粘包问题分析和解决(全)

TCP通信粘包问题分析和解决&#xff08;全&#xff09;在socket网络程序中&#xff0c;TCP和UDP分别是面向连接和非面向连接的。因此TCP的socket编程&#xff0c;收发两端&#xff08;客户端和服务器端&#xff09;都要有成对的socket&#xff0c;因此&#xff0c;发送端为了将…

UML类图符号 各种关系说明以及举例

UML中描述对象和类之间相互关系的方式包括&#xff1a;依赖&#xff0c;关联&#xff0c;聚合&#xff0c;组合&#xff0c;泛化&#xff0c;实现等。表示关系的强弱&#xff1a;组合>聚合>关联>依赖 相互间关系 聚合是表明对象之间的整体与部分关系的关联&#xff0c…

寻找数组中第二大数

设置两个数值来表示最大数和第二大数&#xff0c;在循环比较赋值即可 //找给定数组中第二大的数int get_smax(int *arr,int length) {int max;int smax;if(arr[0] > arr[1]){max arr[0];smax arr[1];}else{max arr[1];smax arr[0];}for(int i 2; i < length; i){if(…

timerfd API使用总结

timerfd 介绍 timerfd 是在Linux内核2.6.25版本中添加的接口&#xff0c;其是Linux为用户提供的一个定时器接口。这个接口基于文件描述符&#xff0c;所以可以被用于select/poll/epoll的场景。当使用timerfd API创建多个定时器任务并置于poll中进行事件监听&#xff0c;当没有可…

#if/#else/#endif

在linux环境下写c代码时会尝试各种方法或调整路径&#xff0c;需要用到#if #include<stdio.h>int main(){int i; #if 0i 1; #elsei 2; #endifprintf("i %d",i);return 0; } 有时候会调整代码&#xff0c;但是又不是最终版本的更换某些值&#xff0c;就需要注…

内存分配调用

通过函数给实参分配内存&#xff0c;可以通过二级指针实现 #include<stdio.h> #incldue<stdlib.h>void getheap(int *p) //错误的模型 {p malloc(100); }void getheap(int **p) //正确的模型 {*p malloc(100); } int main() {int *p NULL;getheap(&p);free(p…

ESP传输模式拆解包流程

一、 ESP简介ESP&#xff0c;封装安全载荷协议(Encapsulating SecurityPayloads)&#xff0c;是一种Ipsec协议&#xff0c;用于对IP协议在传输过程中进行数据完整性度量、来源认证、加密以及防回放攻击。可以单独使用&#xff0c;也可以和AH一起使用。在ESP头部之前的IPV4…

结构体成员内存对齐

#include<stdio.h> struct A {int A; };int main() {struct A a;printf("%d\n",sizeof(a));return 0; } 运行结果&#xff1a;4 #include<stdio.h> struct A {int a;int b&#xff1b; };int main() {struct A a;printf("%d\n",sizeof(a))…

C库函数-fgets()

函数声明&#xff1a;char *fgets(char *str,int n,FILE *stream) 函数介绍&#xff1a;从指定的stream流中读取一行&#xff0c;并把它存储在str所指向的字符串中。当读取到&#xff08;n-1&#xff09;个字符时&#xff0c;获取读取到换行符时&#xff0c;或者到达文件末尾时…

linux内核netfilter模块分析之:HOOKs点的注册及调用

1: 为什么要写这个东西?最近在找工作,之前netfilter 这一块的代码也认真地研究过&#xff0c;应该每个人都是这样的你懂 不一定你能很准确的表达出来。 故一定要化些时间把这相关的东西总结一下。 0&#xff1a;相关文档linux 下 nf_conntrack_tuple 跟踪记录 其中可以根据内…

指定结构体元素的位字段

struct B {char a:4; //a这个成员值占了4bitchar b:2;char c:2; } 占了1个字节 struct B {int a:4; //a这个成员值占了4bitchar b:2;char c:2; } 占了8个字节 控制LED灯的结构体&#xff1a; struct E {char a1:1;char a2:1;char a3:1;char a4:1;char a5:1;char a6:1;char a7:1…

网络抓包工具 wireshark 入门教程

Wireshark&#xff08;前称Ethereal&#xff09;是一个网络数据包分析软件。网络数据包分析软件的功能是截取网络数据包&#xff0c;并尽可能显示出最为详细的网络数据包数据。Wireshark使用WinPCAP作为接口&#xff0c;直接与网卡进行数据报文交换。网络管理员使用Wireshark来…

结构体中指针

结构体中带有指针的情况 #include<stdio.h>struct man {char *name;int age; };int main() {struct man m {"tom",20};printf("name %s, age %d\n",m.name,m.age);return 0; } 运行结果&#xff1a; exbotubuntu:~/wangqinghe/C/20190714$ gcc st…

python使用opencv提取视频中的每一帧、最后一帧,并存储成图片

提取视频每一帧存储图片 最近在搞视频检测问题&#xff0c;在用到将视频分帧保存为图片时&#xff0c;图片可以保存&#xff0c;但是会出现(-215:Assertion failed) !_img.empty() in function cv::imwrite问题而不能正常运行&#xff0c;在检查代码、检查路径等措施均无果后&…

结构体参数

结构体作为函数参数&#xff1a; #include<stdio.h> #include<stdlib.h> #include<string.h>struct student {char name[10];int age; };void print_student(struct student s) {printf("name %s,age %d\n",s.name,s.age); } void set_studen…

线程间通信之eventfd

线程间通信之eventfd man手册中的解释&#xff1a; eventfd()创建了一个“eventfd对象”&#xff0c; 通过它能够实现用户态程序间(我觉得这里主要指线程而非进程)的等待/通知机制&#xff0c;以及内核态向用户态通知的机制&#xff08;未考证&#xff09;。 此对象包含了一个…