线程池实现简单案例(C语言)

本文介绍了线程池的概念、作用和组成,详细讲解了一个使用C语言实现的线程池案例,包括线程回调函数、线程池创建与销毁、任务队列管理等关键部分,展示了如何通过线程池提高并发处理效率。

原理

        线程池顾名思义就是一个存放线程的池子。为什么会有线程池?在这里笔者想强调一点:任何一项新的技术的产生都是为了解决某种问题的!那么线程池的存在肯定也是为了解决项目中存在的某种问题的。

        我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。线程池就是专门解决这种问题的。线程池中的线程创建一次多次复用,直至不需要多线程时(大概率进程结束)再全部销毁。

组成

        线程池的组成:线程队列、任务队列、管理者。

        线程队列:主要是创建的线程队列,用于线程复用,不频繁申请、释放。

        任务队列:需要程序执行的任务组成的队列。

        管理者:用于协调、调度线程执行任务的分配。

案例

        该案例是主线程创建任务,然后线程池管理线程去执行任务,执行完退出。该案例中涉及线程控制的一些知识,详见:Linux的线程控制_码农诗人的博客-优快云博客,代码pthread_pool.c如下:

/**************************************************
*** author : lijd
*** date   : 2022-09-07
**************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define	PTHREAD_NUM		3

// 链表的头插
#define LL_ADD(item, list) do{	\
	item->prev = NULL;			\
	item->next = list;			\
	if(list != NULL) list->prev = item;	\
	list = item;				\
} while(0)

// 链表删除item节点
#define LL_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)

// 线程队列结构
struct NWORKER{
	pthread_t thread;
	struct NMANAGER *pool;
	
	// 线程池是否启停止
	int terminate;
	
	struct NWORKER *prev;
	struct NWORKER *next;
};

// 任务队列结构
struct NJOB{
	void (*func)(void *);
	void *user_data;
	
	struct NJOB *prev;
	struct NJOB *next;
};

// 管理对象结构
struct NMANAGER{
	// 线程链表
	struct NWORKER *workers;
	
	// 任务链表
	struct NJOB *jobs;
	
	// 条件变量
	pthread_cond_t jobs_cond;
	// 线程互斥锁
	pthread_mutex_t jobs_mutex;
};

typedef struct NMANAGER nThreadPool;

// 线程回调函数
static void *nThreadCallback(void *arg)
{
	printf("---------------------nThreadCallback entry---------------thread ID:%ld\n", pthread_self());
	struct NWORKER *worker = (struct NWORKER *)arg;
	
	while(1)
	{
		pthread_mutex_lock(&worker->pool->jobs_mutex);

        // 循环检测任务链表是否为空
		while(worker->pool->jobs == NULL){

            // 任务链表为空时工作线程是否终止
			if(worker->pool->workers->terminate) break;
            
            // 任务链表为空且工作线程不终止,则线程阻塞等待
            // pthread_cond_wait 原子调用: 等待条件变量, 解除锁, 然后阻塞
            // 当 pthread_cond_wait 返回,则条件变量有信号,同时上锁
			pthread_cond_wait(&worker->pool->jobs_cond, &worker->pool->jobs_mutex);
		}
		
        /* 该 if 条件有2个作用:
		   1、如果任务链表不为空,线程需要终止,则结束线程
		   2、如果线程条件等待刚被唤醒,且需要终止则结束线程
		*/
		if(worker->pool->workers->terminate){
			pthread_mutex_unlock(&worker->pool->jobs_mutex);
			break;
		}
		
        // 当任务不为空且当前线程不终止则执行任务
		struct NJOB *job = worker->pool->jobs;
		LL_REMOVE(job, worker->pool->jobs);
		pthread_mutex_unlock(&worker->pool->jobs_mutex);

		job->func(job->user_data);
	}
	
    // 释放工作线程内存结构并终止线程
	free(worker);
	printf("---------------------nThreadCallback exit---------------thread ID:%ld\n", pthread_self());
	pthread_exit(NULL);
}

// 创建线程池
int nThreadPoolCreate(nThreadPool *pool, int numWorkers)
{
	if(numWorkers < 1)	numWorkers = 1;
	
	if(pool == NULL) return -1;
	memset(pool, 0, sizeof(nThreadPool));
	
	pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
	memcpy(&pool->jobs_cond, &blank_cond, sizeof(pthread_cond_t));
	
	pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
	memcpy(&pool->jobs_mutex, &blank_mutex, sizeof(pthread_mutex_t));
	
	int i = 0;
	for(i = 0; i < numWorkers; i++)
	{
		struct NWORKER *worker = (struct NWORKER *)malloc(sizeof(struct NWORKER));
		
		if(worker == NULL){
			perror("malloc");
			return -2;
		}
		
		memset(worker, 0, sizeof(struct NWORKER));
		worker->pool = pool;
		
		int ret = pthread_create(&worker->thread, NULL, nThreadCallback, worker);
		if(ret){
			perror("pthread_create");
			return -3;
		}
		
		LL_ADD(worker, pool->workers);
	}
	printf("---------------------nThreadPoolCreate success!---------------------\n");
	return 0;
}

int nThreadPoolDestroy(nThreadPool *pool)
{
	struct NWORKER *worker = NULL;
	for(worker = pool->workers; worker != NULL; worker = worker->next){
		worker->terminate = 1;
	}
	
	pthread_mutex_lock(&pool->jobs_mutex);
	pthread_cond_broadcast(&pool->jobs_cond);	
	pthread_mutex_unlock(&pool->jobs_mutex);
}

void nThreadPoolPush(nThreadPool *pool, struct NJOB *job)
{
	pthread_mutex_lock(&pool->jobs_mutex);
	LL_ADD(job, pool->jobs);
	pthread_cond_signal(&pool->jobs_cond);
	pthread_mutex_unlock(&pool->jobs_mutex);
}

#if 1

// 任务回调函数
static void *my_jobFunc(void *user_data)
{
	if(user_data == NULL)	return;
	printf("thread ID:%ld, data:%d\n", pthread_self(), *(int *)user_data);
	sleep(1);
}

int main()
{
	nThreadPool *pool = (nThreadPool *)malloc(sizeof(struct NMANAGER));
	
	int ret = nThreadPoolCreate(pool, PTHREAD_NUM);
	
	if(ret != 0)
	{
		return -1;
	}
	printf("-----------------Main thread ID:%ld----------------\n", pthread_self());
	int i = 0;
	for(i = 0; i < 10; i++)
	{
		struct NJOB *job_node = (struct NJOB *)malloc(sizeof(struct NJOB));
		if(job_node == NULL)
		{
			perror("malloc job_node");
			return -2;
		}
		
		job_node->func = my_jobFunc;
		
		job_node->user_data = (void *)malloc(sizeof(int));
		memcpy(job_node->user_data, &i, sizeof(int));
		
		nThreadPoolPush(pool, job_node);
	}
	
	while(pool->jobs != NULL)
	{
		sleep(5);
	}
	
	nThreadPoolDestroy(pool);
	
	sleep(2);
	printf("-----------------Main thread Exit----------------\n");
	return 0;
}

#endif

执行结果如下:

        

        麻雀虽小五脏俱全,至此一个简单的C语言实现的线程池案例成功完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值