先理清思路:
线程池需要维护一个任务队列,允许配置活动的线程数,线程池从任务队列中取任务,根据拿到的任务执行响应处理,所以每个任务是这样一个结构体:
typedef void* (*FUNC_POINT)(void* arg); //定义一个函数指针
typedef struct Task //任务结构体,
{
//void* (*Handler)(void* arg); //处理任务的函数
FUNC_POINT Handler;
void* url; //参数
struct Task* next; //先一个Task的指针
}Task;
线程池Pool也是一个结构体,该结构体如下:
typedef struct Pool
{
Task* head; //任务队列头指针,,指向任务队列的第一个任务
Task* tail; //任务队列尾指针,,指向任务队列的最后一个任务
int maxThreads; //线程池的最大线程个数
int workThreads; //线程池当前的线程数
int freeThreads; //线程池空闲的线程数(指的是:已经创建,并且已经把自己任务完成了的线程)
pthread_mutex_t mut;
pthread_cond_t cond;
int destroy; //线程池销毁标志(值为1销毁)
}Pool;
所以逻辑是这样的:
我们创建一个线程池,然后往线程池里加任务。然后创建线程,线程从线程池结构体里取任务(同时只能由一个线程取任务),取到任务之后调用task结构体中的函数就可。
threadpool_add(Pool* pool, FUNC_POINT fun, void *url);//添加任务到线程池
第一个参数是线程池结构体指针,
第二个参数任务处理函数,就是线程拿到任务之后执行什么任务;
第三个参数是任务处理函数的参数的地址(因为我们要传什么参数需要视情况而定,可能是int,char*或者其他,所以定义一个void*指针,把参数地址传过去就行了)
具体代码如下:
threadpool.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <wait.h>
#include <time.h>
#include <errno.h>
#include <sys/time.h>
typedef void* (*FUNC_POINT)(void* arg); //定义一个函数指针
typedef struct Task //任务结构体,
{
//void* (*Handler)(void* arg); //处理任务的函数
FUNC_POINT Handler;
void* url; //参数
struct Task* next; //先一个Task的指针
}Task;
typedef struct Pool
{
Task* head; //任务队列头指针,,指向任务队列的第一个任务
Task* tail; //任务队列尾指针,,指向任务队列的最后一个任务
int maxThreads; //线程池的最大线程个数
int workThreads; //线程池当前的线程数
int freeThreads; //线程池空闲的线程数(指的是:已经创建,并且已经把自己任务完成了的线程)
pthread_mutex_t mut;
pthread_cond_t cond;
int destroy; //线程池销毁标志(值为1销毁)
}Pool;
void* thread_Handler(void* arg); //线程处理函数
void thread_init(Pool* pool, int max); //线程初始化
void* thread_Handler(void* arg)
{
int timeout;
timeout = 0;
Pool* pool = (Pool*)arg;
while(1)
{
//上锁
pthread_mutex_lock(&pool->mut);
//增加当前的空闲线程数
pool->freeThreads++;
//如果现在没有任务,并且也没有受到线程池销毁的通知
while(pool->head == NULL && pool->destroy == 0)
{
printf("任务队列空!thread:0x%0x is waiting\n",pthread_self());
// int err;
// err = pthread_cond_wait(&pool->cond, &pool->mut);
// if(err != 0){
// printf("pthread_cond_wait is fail\n");
// break;
// }
// break;
//此处使用pthread_cond_timewait。指定等待指定的时间
//必须要等,虽然此时任务队列为空,但可能有的任务还没到任务队列,还在路上
struct timespec tsp;
struct timeval now;
gettimeofday(&now, NULL);//获取当前时间
tsp.tv_sec = now.tv_sec;
tsp.tv_nsec = now.tv_usec * 1000;
tsp.tv_sec += 10; //指定等待时间为10s
int status;
status = pthread_cond_timedwait(&pool->cond, &pool->mut, &tsp);
if(status == ETIMEDOUT){
timeout = 1;
printf("%d freeThreads\n", pool->freeThreads);
break;
}
}
//如果有任务
if(pool->head != NULL){
pool->freeThreads--;
printf("拿到一个任务\n");
Task* tmp = pool->head;
pool->head = tmp->next;
//开始执行任务函数
//此处要解锁,执行任务需要一定时间,这段时间内要允许添加任务
//其他消费者线程能够进入等待任务
pthread_mutex_unlock(&pool->mut);
tmp->Handler(tmp->url);
free(tmp);
pthread_mutex_lock(&pool->mut);
}
//如果现在没有任务,但是收到线程池销毁的通知
if(pool->head == NULL && pool->destroy == 1){
printf("销毁通知\n");
printf("%d workThreads\n",pool->workThreads);
pool->workThreads--;
if(pool->workThreads == 0){
pthread_cond_signal(&pool->cond);
}
pthread_mutex_unlock(&pool->mut);
break; //退出循环
}
//等待超时
if(timeout == 1 && pool->head == NULL){
printf("thread:0x%0x waiting timeout\n",pthread_self());
pool->workThreads--;
pthread_cond_signal(&pool->cond);
pthread_mutex_unlock(&pool->mut);
break;
}
//解锁
pthread_mutex_unlock(&pool->mut);
}
printf("thread: 0x%0x is exiting!\n", (int)pthread_self());
return NULL;
}
void thread_init(Pool* pool, int max)
{
//初始化条件变量和互斥
if(pthread_mutex_init(&pool->mut, NULL) != 0)
{
perror("mutex_init");
return;
}
if(pthread_cond_init(&pool->cond, NULL) != 0)
{
perror("cond_init");
return;
}
//初始化线程池
pool->workThreads = 0;
pool->freeThreads = 0;
pool->maxThreads = max;
pool->head = NULL;
pool->tail = NULL;
pool->destroy = 0;
}
void thread_add(Pool* pool, FUNC_POINT func, void* url)
{
//生成一个新任务
printf("新任务!\n");
Task* newTask = (Task*)malloc(sizeof(Task));
newTask->Handler = func;
newTask->url = url;
newTask->next = NULL;
//把新任务添加到任务队列
pthread_mutex_lock(&pool->mut);
//如果当前任务队列是空的
if(pool->head == NULL)
pool->head = newTask;
else //否则,新的任务放在队列最后
pool->tail->next = newTask;
pool->tail = newTask; //更新尾指针
//唤醒空闲线程
if(pool->freeThreads > 0){
// 唤醒
pthread_cond_signal(&pool->cond);
}else if((pool->workThreads) < (pool->maxThreads)){
// 创建新线程
printf("创建线程\n");
pthread_t id;
pthread_create(&id, NULL, thread_Handler, pool);
pool->workThreads++;
}
pthread_mutex_unlock(&pool->mut);
}
void thread_destroy(Pool* pool)
{
if(pool->destroy == 1)//如果该线程池已经销毁过了,直接退出
return;
//销毁线程池
printf("%d workThreads\n",pool->workThreads);
pthread_mutex_lock(&pool->mut);
if(pool->maxThreads > 0){
if(pool->freeThreads > 0){
pthread_cond_broadcast(&pool->cond);//唤醒所有线程,检查是否还有工作需要完成
}
while(pool->workThreads > 0){
pthread_cond_wait(&pool->cond, &pool->mut); //如果有在工作的线程,需要等待
}
}
pool->destroy = 1;//到此处说明所有线程已经完成任务,workThreads=0。退出标志设为一
int err;
err = pthread_mutex_unlock(&pool->mut);
printf("err=%d\n",err);
err = pthread_mutex_destroy(&pool->mut);
printf("err=%d,互斥量销毁\n",err);
err = pthread_cond_destroy(&pool->cond);
printf("err=%d,条件变量销毁\n",err);
printf("线程池已销毁\n");
}
threadpool.c
#include "pthreadpool.h"
/*
*我的实现思路:
*线程池结构体是Pool,配置活动线程数是通过main函数参数传入的
*线程池内维护了一个任务队列,,每个任务是一个结构体,成员是任务处理函数的地址,参数,和指向下一个任务的指针。
*活动的线程依次从任务队列取任务,
*每个线程取到任务之后执行任务处理函数,该函数fork()一个子进程,由子进程执行程序替换,
*完成工作的线程在任务队列为空的情况下会等到10秒,减少工作线程数量,解锁,然后线程就退出,
*/
/*
*解决线程池的销毁工作:(这一次解决了前一次存在的问题:1,线程没有完成任务情况下,main函数退出
2,在线程池销毁函数中陷入死循环)
*
* 解决方法:(这里在参考了CSDN()[原出处](https://blog.csdn.net/m0_38126105/article/details/79251832)上一个人实现线程池时,对线程池销毁的做法)
* 线程销毁函数在main函数中主动调用,在销毁函数内等待所有线程完成任务都成为空闲线程,并且
* workthreads==0时才会退出。解决了main函数提前退出的问题。
*/
char URL[][100] = {
{"https://dl.softmgr.qq.com/original/Browser/QQBrowser_Setup_Qqpcmgr_10.4.3587.400.exe"},
{"https://dl.softmgr.qq.com/original/Browser/Firefox_Setup_68.0.1_bzb32.exe"},
{"https://dl.softmgr.qq.com/original/Development/npp.7.7.1.Installer.exe"},
{"https://sm.myapp.com/original/Development/epp500_0651_64bit-5.0.651.0.exe"},
{"https://sm.myapp.com/original/Download/fhsetup_8843-3.0.2.exe"},
{"https://dl.softmgr.qq.com/original/Compression/BANDIZIP-SETUP_6.24.0.1.EXE"},
{"https://dl.softmgr.qq.com/original/Compression/7z1900-x64.exe"},
{"https://sm.myapp.com/original/Compression/SpeedZipSetupV2.1.6.6.exe"},
{"https://sm.myapp.com/original/Compression/WinZip-cn_20.0.12033.exe"},
{"https://dl.softmgr.qq.com/original/Compression/winrar-x64-571scp.exe"}
};
void* fun(void* arg)//任务处理函数
{
pid_t id;
if((id = fork())<0){
perror("fork");
return NULL;
}else if(id == 0){
execlp("wget", "wget", "-q", arg, NULL);
}else{
wait(NULL);
}
free(arg);
return NULL;
}
//*******************************************************//
//**********************main函数*************************//
//*******************************************************//
int main(int argc, char* argv[])
{
if(argc != 2){
printf("Error:[./pthreadpool] [threadnums]\n");
return 1;
}
int threadNums;
threadNums = atoi(argv[1]);
Pool pool;
thread_init(&pool, threadNums);
int i;
for(i=0; i<10; ++i)
{
char* ret = (char*)malloc(sizeof(char)*100);
memcpy(ret, URL[i], 100);
thread_add(&pool, fun, ret);
}
signal(SIGCHLD, NULL);
thread_destroy(&pool);
printf("main is over!\n");
return 0;
}
相关问题处理分析: