2020-09-13 Linux 线程池的原理及实现

本文详细介绍了一种基于C语言在Linux系统下实现线程池的方法。文章首先解释了线程池的必要性,随后提供了具体的代码实现,包括线程池的初始化、任务添加、线程销毁等功能。通过实例演示了线程池的使用过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考:https://blog.youkuaiyun.com/lmh12506/article/details/7753952

/*
什么时候需要创建线程池呢?简单的说,如果一个应用需要频繁的创建和销毁线程,而任务执行的时间又非常短,这样线程创建和销毁的带来的开销就不容忽视,这时也是线程池该出场的机会了。如果线程创建和销毁时间相比任务执行时间可以忽略不计,则没有必要使用线程池了。

   下面是Linux系统下用C语言创建的一个线程池。线程池会维护一个任务链表(每个PthreadWorker结构就是一个任务)。

   pool_init()函数预先创建好max_thread_num个线程,每个线程执thread_routine ()函数。该函数中

    while (pool->cur_queue_size == 0)
    {
           pthread_cond_wait (&(pool->queue_ready),&(pool->queue_lock));
    }


表示如果任务链表中没有任务,则该线程出于阻塞等待状态。否则从队列中取出任务并执行。
   
   pool_add_worker()函数向线程池的任务链表中加入一个任务,加入后通过调用pthread_cond_signal (&(pool->queue_ready))唤醒一个出于阻塞状态的线程(如果有的话)。
   
   pool_destroy ()函数用于销毁线程池,线程池任务链表中的任务不会再被执行,但是正在运行的线程会一直把任务运行完后再退出。
*/

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <assert.h>

#define PTHREAD_TASK_MAX_NUM 15
/*
*线程池里所有运行和等待的任务都是一个 PthreadWorker
*由于所有任务都在链表里,所以是一个链表结构
*/
typedef struct worker
{
	int type;
	void *(*process)(void *arg); /*回调函数,任务运行时会调用此函数,注意也可声明成其它形式*/
	void *arg; /*回调函数的参数*/
	struct worker *next; /* 线程池任务链表 */
} PthreadWorker;


/*线程池结构*/
typedef struct
{
	pthread_mutex_t queue_lock;
	pthread_cond_t queue_ready;

	PthreadWorker *queue_head; /*链表结构,线程池中所有等待任务,队列头*/

	int shutdown; /*是否销毁线程池*/
	pthread_t *threadid;
	int max_thread_num; /*线程池中允许的活动线程数目*/
	int cur_queue_size; /*当前等待队列的任务数目*/
} PthreadPool;


int pool_add_worker(void *(*process)(void *arg), void *arg);
void *thread_routine(void *arg);

static PthreadPool *g_pool = NULL;

// 创建线程池,并初始化
void pool_init(int max_thread_num)
{
	g_pool =(PthreadPool *) malloc(sizeof(PthreadPool));
	if(g_pool == NULL) {
		printf("malloc thread g_pool failed");
		return;
	}
	pthread_mutex_init(&(g_pool->queue_lock), NULL);
	pthread_cond_init(&(g_pool->queue_ready), NULL);

	g_pool->queue_head = NULL;

	g_pool->max_thread_num = max_thread_num;
	g_pool->cur_queue_size = 0;
	g_pool->shutdown = 0;

	g_pool->threadid =(pthread_t *) malloc(max_thread_num * sizeof(pthread_t));
	int i = 0;
	for (i = 0; i < max_thread_num; i++) {
		printf("create pthread %d\n", i);
		pthread_create(&(g_pool->threadid[i]), NULL, thread_routine, NULL);
	}
}


/*向线程池中加入任务*/
// 即:将要处理的任务(函数、参数)插入到线程池的任务队列中:PthreadWorker *queue_head;

// 该函数需作为公共模块,供其他功能模块函数调用
int pool_add_worker(void *(*process)(void *arg), void *arg)
{
	/* 1. 构造一个新任务*/
	PthreadWorker *newWorker =(PthreadWorker *) malloc(sizeof(PthreadWorker));
	// 构造任务头
	newWorker->type = (*(int *)arg) % 3;
	newWorker->process = process;
	newWorker->arg = arg;
	newWorker->next = NULL;/*别忘置空*/

	pthread_mutex_lock(&(g_pool->queue_lock));
	/* 2. 将任务加入到等待队列中*/
	PthreadWorker *member = g_pool->queue_head;
	if(member != NULL)	{
		while(member->next != NULL) {
			member = member->next;
		}
		member->next = newWorker;
	} else {
		g_pool->queue_head = newWorker;
	}
	assert(g_pool->queue_head != NULL);

	g_pool->cur_queue_size++;
	printf("[%s], add task count: g_pool->cur_queue_size: %d\n", __FUNCTION__, g_pool->cur_queue_size);
	pthread_mutex_unlock(&(g_pool->queue_lock));
	/* 3. 等待队列中有任务了,唤醒一个等待线程;
		  注意:如果所有线程都在忙碌,这句没有任何作用		????? */
	pthread_cond_signal(&(g_pool->queue_ready));
	return 0;
}

/*销毁线程池,等待队列中的任务不会再被执行,但是正在运行的线程会一直
把任务运行完后再退出*/
int pool_destroy()
{
	if(g_pool->shutdown) {
		return -1;/*防止两次调用*/
	}
	g_pool->shutdown = 1;

	/*唤醒所有等待线程,线程池要销毁了*/
	pthread_cond_broadcast(&(g_pool->queue_ready));

	/*阻塞等待线程退出,否则就成僵尸了*/
	int i;
	for(i = 0; i < g_pool->max_thread_num; i++) {
		pthread_join(g_pool->threadid[i], NULL);
	}
	free(g_pool->threadid);

	/*销毁等待队列*/
	PthreadWorker *head = NULL;
	while(g_pool->queue_head != NULL) {
		head = g_pool->queue_head;
		g_pool->queue_head = g_pool->queue_head->next;
		free(head);
	}
	/*条件变量和互斥量也别忘了销毁*/
	pthread_mutex_destroy(&(g_pool->queue_lock));
	pthread_cond_destroy(&(g_pool->queue_ready));

	free(g_pool);
	/*销毁后指针置空是个好习惯*/
	g_pool=NULL;
	return 0;
}


void *thread_routine(void *arg)
{
	printf("starting thread 0x%x\n",(unsigned int)pthread_self());
	while (1)
	{
		pthread_mutex_lock(&(g_pool->queue_lock));
		/*如果等待队列为0并且不销毁线程池,则处于阻塞状态; 
		注意:pthread_cond_wait是一个原子操作,等待前会解锁,唤醒后会加锁*/
		while (g_pool->cur_queue_size == 0 && !g_pool->shutdown) {
			printf("thread 0x%x is waiting\n\n",(unsigned int)pthread_self());
			pthread_cond_wait(&(g_pool->queue_ready), &(g_pool->queue_lock));
		}

		/*线程池要销毁了*/
		if(g_pool->shutdown)	{
			/*遇到break,continue,return等跳转语句,千万不要忘记先解锁*/
			pthread_mutex_unlock(&(g_pool->queue_lock));
			printf("thread 0x%x will exit\n",(unsigned int)pthread_self());
			pthread_exit(NULL);
		}

		printf("\nthread 0x%x is starting to work\n",(unsigned int)pthread_self());
		assert(g_pool->cur_queue_size != 0);
		assert(g_pool->queue_head != NULL);

		/*等待队列长度减去1,并取出链表中的头元素*/
		g_pool->cur_queue_size--;
		printf("[%s], minus task count: g_pool->cur_queue_size: %d\n", __FUNCTION__, g_pool->cur_queue_size);
		// 如下两句为取出队列中的任务
		PthreadWorker *task = g_pool->queue_head;
		g_pool->queue_head = task->next;
		pthread_mutex_unlock(&(g_pool->queue_lock));
		
		printf("******task type: %d is handle\n", task->type);
		/*调用回调函数,执行任务*/
		(*(task->process))(task->arg);
		
		// 因任务都是通过 malloc(sizeof(PthreadWorker)) 申请内存插入任务队列的,故处理完一个任务需free()内存;
		free(task);
		task = NULL;
	}
	/*这一句应该是不可达的*/
	pthread_exit(NULL);
}

【测试程序】

void *myprocess(void *arg)
{
	printf("threadid is 0x%x, working on task: %d\n",(unsigned int)pthread_self(), *(int *) arg);
	usleep(300000);/*休息一秒,延长任务的执行时间*/
	printf("threadid is 0x%x, working on task: %d, end...\n",(unsigned int)pthread_self(), *(int *) arg);
	return NULL;
}

int main(int argc, char **argv)
{
	pool_init(3);/*线程池中最多三个活动线程*/

	/*连续向池中投入 PTHREAD_TASK_MAX_NUM 个任务*/
	int *workingnum =(int *) malloc(sizeof(int) * PTHREAD_TASK_MAX_NUM);
	int i;
	for (i = 0; i < PTHREAD_TASK_MAX_NUM; i++) {
		workingnum[i] = i;
		pool_add_worker(myprocess, &workingnum[i]);
	}
	/*等待所有任务完成*/
	usleep(3000000);
	printf("[%s], g_pool->cur_queue_size = %d\n", __FUNCTION__, g_pool->cur_queue_size);
	/*销毁线程池*/
	pool_destroy();

	free(workingnum);
	return 0;
}

【编译】

zll@zll-Lenovo:~/Pro_Soft$
zll@zll-Lenovo:~/Pro_Soft$ gcc 05pthread_pool.c -o 05pthread_pool -lpthread
zll@zll-Lenovo:~/Pro_Soft$

 

 

【运行结果】

zll@zll-Lenovo:~/Pro_Soft$ ./05pthread_pool 
create pthread 0
create pthread 1
create pthread 2
starting thread 0x9be02700
thread 0x9be02700 is waiting

[pool_add_worker], add task count: g_pool->cur_queue_size: 1
[pool_add_worker], add task count: g_pool->cur_queue_size: 2
starting thread 0x9b601700
[pool_add_worker], add task count: g_pool->cur_queue_size: 3
[pool_add_worker], add task count: g_pool->cur_queue_size: 4
[pool_add_worker], add task count: g_pool->cur_queue_size: 5
[pool_add_worker], add task count: g_pool->cur_queue_size: 6
[pool_add_worker], add task count: g_pool->cur_queue_size: 7
[pool_add_worker], add task count: g_pool->cur_queue_size: 8
[pool_add_worker], add task count: g_pool->cur_queue_size: 9
[pool_add_worker], add task count: g_pool->cur_queue_size: 10
[pool_add_worker], add task count: g_pool->cur_queue_size: 11
[pool_add_worker], add task count: g_pool->cur_queue_size: 12
[pool_add_worker], add task count: g_pool->cur_queue_size: 13

thread 0x9b601700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 12
******task type: 0 is handle
threadid is 0x9b601700, working on task: 0
starting thread 0x9c603700

thread 0x9be02700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 11
******task type: 1 is handle
threadid is 0x9be02700, working on task: 1
[pool_add_worker], add task count: g_pool->cur_queue_size: 12
[pool_add_worker], add task count: g_pool->cur_queue_size: 13

thread 0x9c603700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 12
******task type: 2 is handle
threadid is 0x9c603700, working on task: 2
threadid is 0x9b601700, working on task: 0, end...

thread 0x9b601700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 11
******task type: 0 is handle
threadid is 0x9b601700, working on task: 3
threadid is 0x9be02700, working on task: 1, end...

thread 0x9be02700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 10
******task type: 1 is handle
threadid is 0x9be02700, working on task: 4
threadid is 0x9c603700, working on task: 2, end...

thread 0x9c603700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 9
******task type: 2 is handle
threadid is 0x9c603700, working on task: 5
threadid is 0x9b601700, working on task: 3, end...

thread 0x9b601700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 8
******task type: 0 is handle
threadid is 0x9b601700, working on task: 6
threadid is 0x9be02700, working on task: 4, end...

thread 0x9be02700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 7
******task type: 1 is handle
threadid is 0x9be02700, working on task: 7
threadid is 0x9c603700, working on task: 5, end...

thread 0x9c603700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 6
******task type: 2 is handle
threadid is 0x9c603700, working on task: 8
threadid is 0x9b601700, working on task: 6, end...

thread 0x9b601700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 5
******task type: 0 is handle
threadid is 0x9b601700, working on task: 9
threadid is 0x9be02700, working on task: 7, end...

thread 0x9be02700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 4
******task type: 1 is handle
threadid is 0x9be02700, working on task: 10
threadid is 0x9c603700, working on task: 8, end...

thread 0x9c603700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 3
******task type: 2 is handle
threadid is 0x9c603700, working on task: 11
threadid is 0x9b601700, working on task: 9, end...

thread 0x9b601700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 2
******task type: 0 is handle
threadid is 0x9b601700, working on task: 12
threadid is 0x9be02700, working on task: 10, end...

thread 0x9be02700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 1
******task type: 1 is handle
threadid is 0x9be02700, working on task: 13
threadid is 0x9c603700, working on task: 11, end...

thread 0x9c603700 is starting to work
[thread_routine], minus task count: g_pool->cur_queue_size: 0
******task type: 2 is handle
threadid is 0x9c603700, working on task: 14
threadid is 0x9b601700, working on task: 12, end...
thread 0x9b601700 is waiting

threadid is 0x9be02700, working on task: 13, end...
thread 0x9be02700 is waiting

threadid is 0x9c603700, working on task: 14, end...
thread 0x9c603700 is waiting

[main], g_pool->cur_queue_size = 0
thread 0x9b601700 will exit
thread 0x9be02700 will exit
thread 0x9c603700 will exit

 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值