一、线程池作用
1.减少线程的创建销毁次数,减少性能开销:
当你需要限制你应用程序中同时运行的线程数时,线程池非常有用。因为启动一个新线程会带来性能开销,每个线程也会为其堆栈分配一些内存等。为了任务的并发执行,我们可以将这些任务任务传递到线程池,而不是为每个任务动态开启一个新的线程。
2.异步解耦的作用:
以日志系统为例,日志从生成到写入磁盘的主要时间开销在读写IO上,在大量日志需要写入磁盘时,傻傻的等待日志一条一条写入显然是不太科学的。借助线程池,我们可以将任务直接丢给工作线程,由他们去完成这样琐碎的任务,这样就能达到异步解耦的作用。
二、线程池创建数量
线程池中的线程数量是依据什么确定的?
答:参考链接:https://huixxi.github.io/2020/06/02/%E5%B0%8F%E7%99%BD%E8%A7%86%E8%A7%92%EF%BC%9A%E4%B8%80%E6%96%87%E8%AF%BB%E6%87%82%E7%A4%BE%E9%95%BF%E7%9A%84TinyWebServer/#more
线程池中的线程数量最直接的限制因素是中央处理器(CPU)的处理器(processors/cores)的数量N:如果你的CPU是4-cores的,对于CPU密集型的任务(如视频剪辑等消耗CPU计算资源的任务)来说,那线程池中的线程数量最好也设置为4(或者+1防止其他因素造成的线程阻塞);对于IO密集型的任务,一般要多于CPU的核数,因为线程间竞争的不是CPU的计算资源而是IO,IO的处理一般较慢,多于cores数的线程将为CPU争取更多的任务,不至在线程处理IO的过程造成CPU空闲导致资源浪费,公式:最佳线程数 = CPU当前可使用的Cores数 * 当前CPU的利用率 * (1 + CPU等待时间 / CPU处理时间)
二、C语言线程池
下面我们用C写一个基本的线程池,基本功能有线程池创建初始化、任务请求与响应、线程池销毁。
1.首先创建任务相关结构体、工作线程相关结构体与线程池结构体:
// 任务相关结构体
typedef struct _st_Task
{
void *taskId;
void (*do_task)(struct _st_Task *); // 处理任务回调函数
struct _st_Task *prev;
struct _st_Task *next;
}st_Task;
// 工作线程相关结构体
typedef struct _st_Work
{
pthread_t pthreadId;
int shutdown; // 线程终止开关
struct _st_ThreadPoolQueue *threadPoolQueue;
struct _st_Work *prev;
struct _st_Work *next;
}st_Work;
// 线程池结构体
typedef struct _st_ThreadPoolQueue
{
st_Work *workQueue;
st_Task *taskQueue;
pthread_mutex_t mutex;
pthread_cond_t cond;
}st_ThreadPoolQueue;
线程池结构体包含两个链表,分别是工作线程链表和任务链表,通过一个线程池结构体管理整个线程池的工作。
2.线程池的创建
int createThreadPool(st_ThreadPoolQueue *threadPool, int threadNum)
{
if(threadPool == NULL) return -1;
if(threadNum < 1) threadNum = 1;
memset(threadPool, 0, sizeof(st_ThreadPoolQueue));
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
memcpy(&threadPool->mutex, &mutex, sizeof(pthread_mutex_t));
memcpy(&threadPool->cond, &cond, sizeof(pthread_cond_t));
int i;
for(i = 0; i < threadNum; ++i)
{
st_Work *work = (st_Work*)malloc(sizeof(st_Work));
if(work == NULL)
{
perror("malloc error!\n");
return i;
}
memset(work, 0, sizeof(work));
work->threadPoolQueue = threadPool;
int ret = pthread_create(&work->pthreadId, NULL, threadFunc, (void *)work);
if(ret < 0)
{
perror("pthread_create error!\n");
free(work);
return i;
}
LIST_ADD(work, work->threadPoolQueue->workQueue);
}
return 0;
}
3.线程入口函数
void *threadFunc(void *param)
{
st_Work *work = (st_Work *)param;
while(1)
{
pthread_mutex_lock(&work->threadPoolQueue->mutex);
while(work->threadPoolQueue->taskQueue == NULL)
{
if(work->shutdown == 1) break;
pthread_cond_wait(&work->threadPoolQueue->cond, &work->threadPoolQueue->mutex);
}
if(work->shutdown == 1)
{
pthread_mutex_unlock(&work->threadPoolQueue->mutex);
break;
}
st_Task *task = work->threadPoolQueue->taskQueue;
if(task != NULL)
{
LIST_REMOVE(task, work->threadPoolQueue->taskQueue);
}
pthread_mutex_unlock(&work->threadPoolQueue->mutex);
task->do_task(task);
}
free(work);
pthread_exit(NULL);
}
4.线程销毁
void destroyThreadPool(st_ThreadPoolQueue *threadPool)
{
st_Work *work = NULL;
for(work = threadPool->workQueue; work != NULL; work = work->next)
{
work->shutdown = 1;
}
pthread_cond_broadcast(&threadPool->cond);
threadPool->taskQueue = NULL;
threadPool->workQueue = NULL;
pthread_mutex_destroy(&threadPool->mutex);
pthread_cond_destroy(&threadPool->cond);
}
5.完整代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#define D_MAX_THREAD_NUM 10
#define D_MAX_TASK_NUM 100
// 宏函数定义
#define LIST_ADD(item, list) do{ \
item->prev = NULL; \
item->next = list; \
list = item; \
}while(0);
#define LIST_REMOVE(item, list) do{ \
if(item->prev != NULL) item->prev->next = item->next; \
if(item->next != NULL) item->next->prev = item->prev; \
if(list == item) list = item->next; \
item->prev = item->next = NULL; \
}while(0);
// 任务相关结构体
typedef struct _st_Task
{
void *taskId;
void (*do_task)(struct _st_Task *);
struct _st_Task *prev;
struct _st_Task *next;
}st_Task;
// 工作线程相关结构体
typedef struct _st_Work
{
pthread_t pthreadId;
int shutdown;
struct _st_ThreadPoolQueue *threadPoolQueue;
struct _st_Work *prev;
struct _st_Work *next;
}st_Work;
// 线程池结构体
typedef struct _st_ThreadPoolQueue
{
st_Work *workQueue;
st_Task *taskQueue;
pthread_mutex_t mutex;
pthread_cond_t cond;
}st_ThreadPoolQueue;
void *threadFunc(void *param)
{
st_Work *work = (st_Work *)param;
while(1)
{
pthread_mutex_lock(&work->threadPoolQueue->mutex);
while(work->threadPoolQueue->taskQueue == NULL)
{
if(work->shutdown == 1) break;
pthread_cond_wait(&work->threadPoolQueue->cond, &work->threadPoolQueue->mutex);
}
if(work->shutdown == 1)
{
pthread_mutex_unlock(&work->threadPoolQueue->mutex);
break;
}
st_Task *task = work->threadPoolQueue->taskQueue;
if(task != NULL)
{
LIST_REMOVE(task, work->threadPoolQueue->taskQueue);
}
pthread_mutex_unlock(&work->threadPoolQueue->mutex);
task->do_task(task);
}
free(work);
pthread_exit(NULL);
}
static int createThreadPool(st_ThreadPoolQueue *threadPool, int threadNum)
{
if(threadPool == NULL) return -1;
if(threadNum < 1) threadNum = 1;
memset(threadPool, 0, sizeof(st_ThreadPoolQueue));
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
memcpy(&threadPool->mutex, &mutex, sizeof(pthread_mutex_t));
memcpy(&threadPool->cond, &cond, sizeof(pthread_cond_t));
int i;
for(i = 0; i < threadNum; ++i)
{
st_Work *work = (st_Work*)malloc(sizeof(st_Work));
if(work == NULL)
{
perror("malloc error!\n");
return i;
}
memset(work, 0, sizeof(work));
work->threadPoolQueue = threadPool;
int ret = pthread_create(&work->pthreadId, NULL, threadFunc, (void *)work);
if(ret < 0)
{
perror("pthread_create error!\n");
free(work);
return i;
}
LIST_ADD(work, work->threadPoolQueue->workQueue);
}
return 0;
}
void doTask(st_Task *task)
{
if(task == NULL)
{
perror("task is NULL\n");
return;
}
printf("Do taskid = %d, threadid = %d\n", *(int *)task->taskId, pthread_self());
free(task->taskId);
free(task);
}
void inTaskQueue(st_ThreadPoolQueue *threadPool, st_Task *task)
{
pthread_mutex_lock(&threadPool->mutex);
LIST_ADD(task, threadPool->taskQueue);
pthread_cond_signal(&threadPool->cond);
pthread_mutex_unlock(&threadPool->mutex);
}
void destroyThreadPool(st_ThreadPoolQueue *threadPool)
{
st_Work *work = NULL;
for(work = threadPool->workQueue; work != NULL; work = work->next)
{
work->shutdown = 1;
}
pthread_cond_broadcast(&threadPool->cond);
threadPool->taskQueue = NULL;
threadPool->workQueue = NULL;
pthread_mutex_destroy(&threadPool->mutex);
pthread_cond_destroy(&threadPool->cond);
}
int main(int argc, char *argv[])
{
st_ThreadPoolQueue threadPool;
createThreadPool(&threadPool, D_MAX_THREAD_NUM);
// 创建指定数量的任务
int i;
for(i = 0; i < D_MAX_TASK_NUM; ++i)
{
st_Task *task = (st_Task *)malloc(sizeof(st_Task));
if(task == NULL)
{
perror("malloc error\n");
return -1;
}
task->taskId = (int *)malloc(sizeof(int));
*(int *)task->taskId = i;
task->do_task = doTask;
inTaskQueue(&threadPool, task);
sleep(1);
}
getchar();
destroyThreadPool(&threadPool);
return 0;
}