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

原理

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

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

组成

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

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

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

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

案例

        该案例是主线程创建任务,然后线程池管理线程去执行任务,执行完退出。该案例中涉及线程控制的一些知识,详见:Linux的线程控制_码农诗人的博客-CSDN博客,代码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语言实现的线程池案例成功完成。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
线程池是一种用于管理和复用线程的机制,它可以提高多线程程序的性能和可扩展性。线程池中包含一组预先创建的线程,这些线程可以处理任务队列中的任务,而不需要每次都创建和销毁线程线程池的原理如下: 1. 创建线程池:首先,创建一个包含固定数量的线程线程池。这些线程处于空闲状态,等待任务的到来。 2. 提交任务:当有任务需要执行时,将任务提交到线程池。 3. 任务队列:线程池中有一个任务队列,用于存储待执行任务。当有任务提交时,将任务放入队列中。 4. 任务调度:线程池中的空闲线程会从任务队列中获取任务进行执行线程会不断地从任务队列中获取任务,直到队列为空。 5. 执行任务线程任务队列中获取任务后,执行任务。 6. 线程复用:当一个线程完成了一个任务后,它会再次进入空闲状态,等待下一个任务的到来。这样可以避免频繁地创建和销毁线程,提高了性能和效率。 下面是一个简单C语言实现线程池的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #define THREAD_POOL_SIZE 5 typedef struct { void (*task)(void *); void *arg; } Task; typedef struct { pthread_t thread; int is_working; } Worker; Task task_queue[THREAD_POOL_SIZE]; Worker workers[THREAD_POOL_SIZE]; pthread_mutex_t mutex; pthread_cond_t cond; void *worker_thread(void *arg) { while (1) { pthread_mutex_lock(&mutex); // 等待任务到来 while (task_queue[*(int *)arg].task == NULL) { pthread_cond_wait(&cond, &mutex); } // 执行任务 Task task = task_queue[*(int *)arg]; task.task(task.arg); // 清空任务 task_queue[*(int *)arg].task = NULL; pthread_mutex_unlock(&mutex); } } void thread_pool_init() { int i; pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); for (i = 0; i < THREAD_POOL_SIZE; i++) { workers[i].is_working = 0; task_queue[i].task = NULL; pthread_create(&workers[i].thread, NULL, worker_thread, &i); } } void thread_pool_submit(void (*task)(void *), void *arg) { pthread_mutex_lock(&mutex); // 查找空闲线程 int i; for (i = 0; i < THREAD_POOL_SIZE; i++) { if (task_queue[i].task == NULL) { task_queue[i].task = task; task_queue[i].arg = arg; pthread_cond_signal(&cond); break; } } pthread_mutex_unlock(&mutex); } void my_task(void *arg) { int id = *(int *)arg; printf("Task executed by thread %d\n", id); } int main() { int i; thread_pool_init(); // 提交任务 for (i = 0; i < 10; i++) { int *arg = malloc(sizeof(int)); *arg = i; thread_pool_submit(my_task, arg); } // 等待任务完成 sleep(1); return 0; } ``` 这是一个简单线程池实现。在主函数中,我们初始化了线程池,然后提交了10个任务。每个任务都打印出当前执行任务线程ID。最后,我们等待1秒钟,以确保所有任务都被执行完毕。 请注意,这只是一个简单示例,实际上线程池实现可能需要更多的细节和线程安全机制。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值