理解线程池工作原理并(C语言)实现

1.线程池的概念

线程池是由服务器预先创建的一组子线程,线程池中的线程数量应该和CPU数量差不多线程池中的所有子线程都运行着相同的代码。当有新的任务到来时,主线程将通过某种方式选择线程池中的某一个子线程来为之服务。相比于动态的创建子线程,选择一个已经存在的子线程的代价显然要小很多。
主线程使用某种算法主动选择子线程,常用的有随机算法和轮流选取算法;
主线程和所有子线程通过一个共享工作队列来同步,子线程都睡眠在该工作队列上。当有新的任务到来时,主线程将任务添加到工作队列中。这将唤醒正在等待任务的子线程,不过只有一个子线程将获得新任务的“接管权”,他可以从工作队列中取出任务并执行之,而其他子线程将继续睡眠在工作队列上。

2.创建linux项目
  1. 在虚拟机中安装一个linux
  2. 在vs中创建一个vs项目(两个都可)
    在这里插入图片描述
  3. 连接linux
    在这里插入图片描述
  4. 填写虚拟机信息
    在这里插入图片描述
  5. 将linux中的头文件下载到windows上
    在这里插入图片描述
3.定义线程池结构体
//任务结构体
typedef struct Task{
	void (*func)(void* arg);
	void* arg;
}Task;
//线程池结构体
typedef struct ThreadPool{
	//任务队列
	Task* taskQ;
	int queueCapacity;//容量
	int queueSize;//当前任务个数
	int queueFront;	//对头->取数据
	int queueRear;	//队尾->放数据

	pthread_t managerID;	//管理者线程ID
	pthread_t *threadIDs;	//工作线程ID
	int minNum;	//最小线程数
	int maxNum;	//最大线程数
	int busyNum;	//忙的线程数==工作的线程
	int liveNum;	//存活的线程数
	int exitNum;	//要杀死的线程个数
	pthread_mutex_t mutexPool;	//互斥锁,锁整个线程池
	pthread_mutex_t mutexBusy;	//锁busyNum变量,对于灵活修改的变量,需要考虑单独上锁,操作效率会比较高
	//利用条件变量负责阻塞生产者和消费者
	pthread_cond_t notFull;	//任务队列是不是满了
	pthread_cond_t notEmpty;	//任务队列是不是空了

	int shutdown;	//是不是要销毁线程池,销毁1,不销毁为0
}ThreadPool;
4.创建线程池并初始化

pthread_t在linux其实是一个为 unsigned long类型的数,其含义为线程的身份ID,可以通过调用pthread_self()函数获取线程ID 。该函数的步骤为:

  1. 首先为ThreadPool类型的变量开辟空间,若开辟失败结束,否则继续执行;
  2. 为threadIDs变量开辟空间,空间大小为maxNum*pthread_t,分配失败结束,否则将开辟空间的值都设置为0,并继续执行
  3. 为整型变量进行初始化
  4. 为锁和互斥量进行初始化
  5. 为任务队列进行初始化
  6. 创建管理者线程和工作线程(按最小值进行创建)
//创建线程池并初始化
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->minNum = min;
		pool->maxNum = max;
		pool->busyNum = 0;
		pool->liveNum = min;	//和最小个数相等
		pool->exitNum = 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 condition init fail\n");
			break;
		}
		//任务队列
		pool->taskQ = (Task*)malloc(sizeof(Task) * queueSize);
		pool->queueCapacity = queueSize;
		pool->queueSize = 0;
		pool->queueFront = 0;
		pool->queueRear = 0;
		pool->shutdown = 0;

		//创建线程
		pthread_create(&pool->managerID, NULL, manager, pool);//manager和work都是回调函数,后面的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;
}

注:采用do{…}while(0);结构是因为在循环体内部可以使用break替换return,这样可以在循环体外部将之前初始化的堆内存资源进行释放。

5.添加工作线程的任务函数

当pthread_mutex_lock()返回时,该互斥锁已被锁定。线程调用该函数让互斥锁上锁,如果该互斥锁已被另一个线程锁定和拥有,则调用该线程将阻塞,直到该互斥锁变为可用为止。
pthread_cond_wait()用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。
pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。它最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。pthread_cond_broadcast唤醒全部线程。
该函数的工作流程:

  1. 首先将void*类型转换成ThreadPool *;
  2. 因为进入任务函数需要不停的读任务队列,所以需要一个while(1)循环;
  3. 线程池相当于一个共享的资源,每次访问的时候都需要pthread_mutex_lock进行加锁(解锁需要调用unlock方法);
  4. 若当前工作队列为空并且没有被关闭,就需要使用条件变量函数pthread_cond_wait阻塞工作队列;
  5. 判断线程池是否被关闭,如果被关闭需要退出,否则继续执行;
  6. 从工作队列中取出一个任务,并进行保存(从任务队列的头部取,并将对头向后移动);
  7. 执行工作线程
void* worker(void* arg)
{
	ThreadPool* pool = (ThreadPool*)arg;
	while (1){
		pthread_mutex_lock(&pool->mutexPool);
		//当前任务队列是否为空
		//必须要写while循环的原因:
		//假设有5个线程被唤醒,其中的某一个线程判断队列不为空,消费完了之后,
		//任务队列又为空,其他线程需要继续堵塞在这
		while (pool->queueSize==0&&!pool->shutdown) {
			//阻塞工作线程
			pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);

			//判断是不是要销毁线程
			if (pool->exitNum > 0) {
				pool->exitNum--;
				if (pool->liveNum > pool->minNum) {
					pool->liveNum--;
					pthread_mutex_unlock(&pool->mutexPool);
					threadExit(pool);
				}
			}
		}
		//判断线程池是否被关闭了
		if (pool->shutdown) {
			pthread_mutex_unlock(&pool->mutexPool);
			//pthread_exit(NULL);
			threadExit(pool);
		}
		//从任务队列中取出一个任务
		Task task;
		task.func = pool->taskQ[pool->queueFront].func;
		task.arg = pool->taskQ[pool->queueFront].arg;
		//移动头节点(循环队列)
		pool->queueFront = (pool->queueFront + 1) % pool->queueCapacity;//环形队列,取余实现
		pool->queueSize--;
		//解锁
		pthread_cond_signal(&pool->notFull);//生产者对应的条件变量
		pthread_mutex_unlock(&pool->mutexPool);

		printf("thread %ld start working\n",pthread_self());
		pthread_mutex_lock(&pool->mutexBusy);
		pool->busyNum++;
		pthread_mutex_unlock(&pool->mutexBusy);
		//执行
		task.func(task.arg);
		free(task.arg);
		task.arg = NULL;
		printf("thread %ld end working\n", pthread_self());
		pthread_mutex_lock(&pool->mutexBusy);
		pool->busyNum--;
		pthread_mutex_unlock(&pool->mutexBusy);
	}
	return NULL;
}
6.管理者线程的任务函数

该函数的工作流程:

  1. 首先将void*类型转换成ThreadPool *;
  2. 管理者需要按照某种频率去检测,看需不需要对线程池进行调节,而工作线程需要不断去检测;
  3. 取出线程池中任务的数量和当前线程的数量,这一步需要加锁,因为任务数量和当前的线程数量是不断变化的,而最大线程数一旦初始化后是不变的可以不加锁;
  4. 取出忙的线程数,也需要加锁
  5. 按照某种策略动态添加线程
  6. 按照某种策略动态销毁线程
void* manager(void* arg)
{
	ThreadPool* pool = (ThreadPool*)arg;
	while (!pool->shutdown) {
		//每隔3s检测一次
		sleep(3);
		//取出线程池中任务的数量和当前线程的数量
		pthread_mutex_lock(&pool->mutexPool);
		int queueSize = pool->queueSize;
		int liveNum = pool->liveNum;
		pthread_mutex_unlock(&pool->mutexPool);
		
		//取出忙的线程数量
		pthread_mutex_lock(&pool->mutexBusy);
		int busyNum = pool->busyNum;
		pthread_mutex_unlock(&pool->mutexBusy);

		//添加线程
		//任务的个数大于存活的线程个数 && 存活的线程数小于最大线程数
		if (queueSize > liveNum && liveNum < pool->maxNum) {
			pthread_mutex_lock(&pool->mutexPool);
			int counter = 0;
			for (int i = 0; i < pool->maxNum && counter < NUMBER && pool->liveNum < pool->maxNum;i++) {
				if (pool->threadIDs[i]==0) {
					pthread_create(&pool->threadIDs[i], NULL, worker, pool);
					counter++;
					pool->liveNum++;
				}
			}
			pthread_mutex_unlock(&pool->mutexPool);
		}

		//销毁线程
		//忙的线程数乘以2小于存活的线程数 && 存活的线程数大于最小线程数
		if (busyNum * 2 < liveNum && liveNum > pool->minNum) {
			pthread_mutex_lock(&pool->mutexPool);
			pool->exitNum = NUMBER;
			pthread_mutex_unlock(&pool->mutexPool);
			//让工作线程自杀
			for (int i = 0; i < NUMBER; i++) {
				pthread_cond_signal(&pool->notEmpty);
				//把堵塞在条件变量的线程唤醒,向下执行,并让它们自杀,
				/*
				//判断是不是要销毁线程,这一部分代码位于worker函数中
				if (pool->exitNum > 0) {
					pool->exitNum--;
					if (pool->liveNum > pool->minNum) {
						pool->liveNum--;
						pthread_mutex_unlock(&pool->mutexPool);
						threadExit(pool);
					}
				}
				*/
			}
		}
	}
	return NULL;
}
7.线程退出函数

该函数的是为了解决管理者线程在添加线程过程中,找到工作队列中第一id为0的位置,为了能重复使用,需要在线程退出时,将其重新设置为0.其步骤:

  1. 获取当前的线程id
  2. 通过循环找到当前的线程id在工作队列的位置
  3. 并将该线程的id赋值为0
void threadExit(ThreadPool* pool)
{
	//获取当前线程的id
	pthread_t tid = pthread_self();
	for (int i = 0; i < pool->maxNum; i++) {
		if (pool->threadIDs[i] = tid) {
			pool->threadIDs[i] = 0;
			printf("threadExit() called, %ld exiting\n", tid);
			break;
		}
	}
	pthread_exit(NULL);
}
8.向线程池中添加任务

该函数的步骤:

  1. 线程池有可能多个工作线程同时访问,所以先加锁;
  2. 如果当前工作队列已经满了,并且线程池没有被关闭,需要阻塞生产者;
  3. 如果线程池被关闭,直接退出,否则接着执行
  4. 添加任务(需要在队尾进行添加)
  5. 向消费者发送信号,唤醒阻塞在条件变量的那个工作线程
void threadPoolAdd(ThreadPool* pool, void(*func)(void*), void* arg){
	pthread_mutex_lock(&pool->mutexPool);
	while (pool->queueSize == pool->queueCapacity && pool->shutdown == 0) {
		//阻塞生产者线程
		pthread_cond_wait(&pool->notFull, &pool->mutexPool);
	}
	if (pool->shutdown) {
		pthread_mutex_unlock(&pool->mutexPool);
		return;
	}
	//添加任务
	pool->taskQ[pool->queueRear].func = func;
	pool->taskQ[pool->queueRear].arg = arg;
	pool->queueRear = (pool->queueRear + 1) % pool->queueCapacity;//循环队列
	pool->queueSize++;

	pthread_cond_signal(&pool->notEmpty);
	pthread_mutex_unlock(&pool->mutexPool);
}
9.获取线程池的工作线程和活着的线程数量

这两个函数很简单,直接返回线程池结构体的变量值即可

int threadPoolBusyNum(ThreadPool* pool){
	pthread_mutex_lock(&pool->mutexBusy);
	int busyNum = pool->busyNum;
	pthread_mutex_unlock(&pool->mutexBusy);
	return busyNum;
}

int threadPoolAliveNum(ThreadPool* pool){
	pthread_mutex_lock(&pool->mutexPool);
	int liveNum = pool->liveNum;
	pthread_mutex_unlock(&pool->mutexPool);
	return liveNum;
}
10.线程池的销毁

pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果线程已经结束,那么该函数会立即返回。
该函数的步骤:

  1. 首先判断线程池的指针变量是否指向一个有效的地址,如果不是直接退出,否者接着执行;
  2. 关闭线程池
  3. 阻塞回收管理者线程
  4. 唤醒则色的消费者线程(唤醒之后,因为shutdown被置为1,工作线程退出);
  5. 释放申请的堆内存
int threadPoolDestroy(ThreadPool* pool){
	if (pool == NULL) {//指向的不是一块有效的地址
		return -1;
	}
	//关闭线程池
	pool->shutdown = 1;
	//阻塞回收管理者线程
	pthread_join(pool->managerID, NULL);
	//唤醒阻塞的消费者线程
	for (int i = 0; i < pool->liveNum; i++) {
		pthread_cond_signal(&pool->notEmpty);
	}
	//释放堆内存
	if (pool->taskQ) {
		free(pool->taskQ);
	}
	if (pool->threadIDs) {
		free(pool->threadIDs);
	}
	pthread_mutex_destroy(&pool->mutexPool);
	pthread_mutex_destroy(&pool->mutexBusy);
	pthread_cond_destroy(&pool->notEmpty);
	pthread_cond_destroy(&pool->notFull);
	free(pool);
	pool = NULL;
	return 0;
}
11.添加测试代码
#include <stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include "threadpool.h"
void taskfunc(void* arg) {
    int num = *(int*)arg;
    printf("thread %ld is working, number=%d\n", pthread_self(), num);
    sleep(1);
}
int main(){
    //创建线程池
    ThreadPool* pool = threadPoolCreate(3, 10, 100);
    for (int i = 0; i < 100; i++) {
        int* num = (int*)malloc(sizeof(int));
        *num = i + 100;
        threadPoolAdd(pool, taskfunc, num);
    }
    sleep(30);
    threadPoolDestroy(pool);
    return 0;
}
12.调试和编译

在windows环境下编译和执行:
在这里插入图片描述

在这里插入图片描述
在linux环境下编译和执行:
在这里插入图片描述
运行结果
在这里插入图片描述

13.完整代码

main.c

#include <stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include "threadpool.h"
void taskfunc(void* arg) {
    int num = *(int*)arg;
    printf("thread %ld is working, number=%d\n", pthread_self(), num);
    sleep(1);
}
int main(){
    //创建线程池
    ThreadPool* pool = threadPoolCreate(3, 10, 100);
    for (int i = 0; i < 100; i++) {
        int* num = (int*)malloc(sizeof(int));
        *num = i + 100;
        threadPoolAdd(pool, taskfunc, num);
    }
    sleep(30);
    threadPoolDestroy(pool);
    return 0;
}

threadpool.h

#ifndef _THREADPOOL_H
#define _THREADPOOL_H

typedef struct ThreadPool ThreadPool;
//创建线程池并初始化
ThreadPool *threadPoolCreate(int min,int max,int queueSize);
//销毁线程池
int threadPoolDestroy(ThreadPool* pool);
//给线程池添加任务
void threadPoolAdd(ThreadPool* pool, void(*func)(void*), void* arg);
//获取线程池中工作的线程个数
int threadPoolBusyNum(ThreadPool* pool);
//获取线程池中活着的线程的个数
int threadPoolAliveNum(ThreadPool* pool);

void* worker(void* arg);
void* manager(void* arg);
void threadExit(ThreadPool* pool);
#endif // !_THREADPOOL_H


threadpool.c

#include"threadpool.h"
#include<pthread.h>
#include<stdio.h>
#include<malloc.h>
#include<unistd.h>
#include<stdlib.h>

const int NUMBER = 2;
//任务结构体
typedef struct Task{
	void (*func)(void* arg);
	void* arg;
}Task;
//线程池结构体
typedef struct ThreadPool{
	//任务队列
	Task* taskQ;
	int queueCapacity;//容量
	int queueSize;//当前任务个数
	int queueFront;	//对头->取数据
	int queueRear;	//队尾->放数据

	pthread_t managerID;	//管理者线程ID
	pthread_t *threadIDs;	//工作线程ID
	int minNum;	//最小线程数
	int maxNum;	//最大线程数
	int busyNum;	//忙的线程数==工作的线程
	int liveNum;	//存活的线程数
	int exitNum;	//要杀死的线程个数
	pthread_mutex_t mutexPool;	//互斥锁,锁整个线程池
	pthread_mutex_t mutexBusy;	//锁busyNum变量,对于灵活修改的变量,需要考虑单独上锁,操作效率会比较高
	//利用条件变量负责阻塞生产者和消费者
	pthread_cond_t notFull;	//任务队列是不是满了
	pthread_cond_t notEmpty;	//任务队列是不是空了

	int shutdown;	//是不是要销毁线程池,销毁1,不销毁为0
}ThreadPool;
//创建线程池并初始化
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->minNum = min;
		pool->maxNum = max;
		pool->busyNum = 0;
		pool->liveNum = min;	//和最小个数相等
		pool->exitNum = 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 condition init fail\n");
			break;
		}
		//任务队列
		pool->taskQ = (Task*)malloc(sizeof(Task) * queueSize);
		pool->queueCapacity = queueSize;
		pool->queueSize = 0;
		pool->queueFront = 0;
		pool->queueRear = 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;
}

int threadPoolDestroy(ThreadPool* pool){
	if (pool == NULL) {//指向的不是一块有效的地址
		return -1;
	}
	//关闭线程池
	pool->shutdown = 1;
	//阻塞回收管理者线程
	pthread_join(pool->managerID, NULL);
	//唤醒阻塞的消费者线程
	for (int i = 0; i < pool->liveNum; i++) {
		pthread_cond_signal(&pool->notEmpty);
	}
	//释放堆内存
	if (pool->taskQ) {
		free(pool->taskQ);
	}
	if (pool->threadIDs) {
		free(pool->threadIDs);
	}
	pthread_mutex_destroy(&pool->mutexPool);
	pthread_mutex_destroy(&pool->mutexBusy);
	pthread_cond_destroy(&pool->notEmpty);
	pthread_cond_destroy(&pool->notFull);
	free(pool);
	pool = NULL;
	return 0;
}

void threadPoolAdd(ThreadPool* pool, void(*func)(void*), void* arg){
	pthread_mutex_lock(&pool->mutexPool);
	while (pool->queueSize == pool->queueCapacity && pool->shutdown == 0) {
		//阻塞生产者线程
		pthread_cond_wait(&pool->notFull, &pool->mutexPool);
	}
	if (pool->shutdown) {
		pthread_mutex_unlock(&pool->mutexPool);
		return;
	}
	//添加任务
	pool->taskQ[pool->queueRear].func = func;
	pool->taskQ[pool->queueRear].arg = arg;
	pool->queueRear = (pool->queueRear + 1) % pool->queueCapacity;//循环队列
	pool->queueSize++;

	pthread_cond_signal(&pool->notEmpty);
	pthread_mutex_unlock(&pool->mutexPool);
}

int threadPoolBusyNum(ThreadPool* pool){
	pthread_mutex_lock(&pool->mutexBusy);
	int busyNum = pool->busyNum;
	pthread_mutex_unlock(&pool->mutexBusy);
	return busyNum;
}

int threadPoolAliveNum(ThreadPool* pool){
	pthread_mutex_lock(&pool->mutexPool);
	int liveNum = pool->liveNum;
	pthread_mutex_unlock(&pool->mutexPool);
	return liveNum;
}

void* worker(void* arg)
{
	ThreadPool* pool = (ThreadPool*)arg;
	while (1){
		pthread_mutex_lock(&pool->mutexPool);
		//当前任务队列是否为空
		//必须要写while循环的原因:
		//假设有5个线程被唤醒,其中的某一个线程判断队列不为空,消费完了之后,
		//任务队列又为空,其他线程需要继续堵塞在这
		while (pool->queueSize==0&&!pool->shutdown) {
			//阻塞工作线程
			pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);

			//判断是不是要销毁线程
			if (pool->exitNum > 0) {
				pool->exitNum--;
				if (pool->liveNum > pool->minNum) {
					pool->liveNum--;
					pthread_mutex_unlock(&pool->mutexPool);
					threadExit(pool);
				}
			}
		}
		//判断线程池是否被关闭了
		if (pool->shutdown) {
			pthread_mutex_unlock(&pool->mutexPool);
			//pthread_exit(NULL);
			threadExit(pool);
		}
		//从任务队列中取出一个任务
		Task task;
		task.func = pool->taskQ[pool->queueFront].func;
		task.arg = pool->taskQ[pool->queueFront].arg;
		//移动头节点(循环队列)
		pool->queueFront = (pool->queueFront + 1) % pool->queueCapacity;//环形队列,取余实现
		pool->queueSize--;
		//解锁
		pthread_cond_signal(&pool->notFull);//生产者对应的条件变量
		pthread_mutex_unlock(&pool->mutexPool);

		printf("thread %ld start working\n",pthread_self());
		pthread_mutex_lock(&pool->mutexBusy);
		pool->busyNum++;
		pthread_mutex_unlock(&pool->mutexBusy);
		//执行
		task.func(task.arg);
		free(task.arg);
		task.arg = NULL;
		printf("thread %ld end working\n", pthread_self());
		pthread_mutex_lock(&pool->mutexBusy);
		pool->busyNum--;
		pthread_mutex_unlock(&pool->mutexBusy);
	}
	return NULL;
}

void* manager(void* arg)
{
	ThreadPool* pool = (ThreadPool*)arg;
	while (!pool->shutdown) {
		//每隔3s检测一次
		sleep(3);
		//取出线程池中任务的数量和当前线程的数量
		pthread_mutex_lock(&pool->mutexPool);
		int queueSize = pool->queueSize;
		int liveNum = pool->liveNum;
		pthread_mutex_unlock(&pool->mutexPool);
		
		//取出忙的线程数量
		pthread_mutex_lock(&pool->mutexBusy);
		int busyNum = pool->busyNum;
		pthread_mutex_unlock(&pool->mutexBusy);

		//添加线程
		//任务的个数大于存活的线程个数 && 存活的线程数小于最大线程数
		if (queueSize > liveNum && liveNum < pool->maxNum) {
			pthread_mutex_lock(&pool->mutexPool);
			int counter = 0;
			for (int i = 0; i < pool->maxNum && counter < NUMBER && pool->liveNum < pool->maxNum;i++) {
				if (pool->threadIDs[i]==0) {
					pthread_create(&pool->threadIDs[i], NULL, worker, pool);
					counter++;
					pool->liveNum++;
				}
			}
			pthread_mutex_unlock(&pool->mutexPool);
		}

		//销毁线程
		//忙的线程数乘以2小于存活的线程数 && 存活的线程数大于最小线程数
		if (busyNum * 2 < liveNum && liveNum > pool->minNum) {
			pthread_mutex_lock(&pool->mutexPool);
			pool->exitNum = NUMBER;
			pthread_mutex_unlock(&pool->mutexPool);
			//让工作线程自杀
			for (int i = 0; i < NUMBER; i++) {
				pthread_cond_signal(&pool->notEmpty);
				//把堵塞在条件变量的线程唤醒,向下执行,并让它们自杀
				/*
				//判断是不是要销毁线程
				if (pool->exitNum > 0) {
					pool->exitNum--;
					if (pool->liveNum > pool->minNum) {
						pool->liveNum--;
						pthread_mutex_unlock(&pool->mutexPool);
						threadExit(pool);
					}
				}
				*/
			}
		}
	}
	return NULL;
}

void threadExit(ThreadPool* pool)
{
	//获取当前线程的id
	pthread_t tid = pthread_self();
	for (int i = 0; i < pool->maxNum; i++) {
		if (pool->threadIDs[i] = tid) {
			pool->threadIDs[i] = 0;
			printf("threadExit() called, %ld exiting\n", tid);
			break;
		}
	}
	pthread_exit(NULL);
}

视频链接

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZhInen丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值