C语言学习项目——线程池原理及其实现

线程池介绍

线程池是一种用于管理和复用多个线程的技术。它由线程池管理器、工作队列和一组线程组成。

线程池的主要目的是减少线程创建和销毁的开销,并在任务到达时立即执行任务。它以一种优化的方式分配和控制线程,提供了一种高效的并发处理机制。

线程池的工作原理如下:

  1. 创建一个固定数量的线程池,初始化线程池中的线程。
  2. 当有任务到达时,线程池将任务放入工作队列。
  3. 线程池中的空闲线程从工作队列中获取任务并执行。
  4. 执行完任务后,线程返回线程池并等待新的任务。
  5. 当线程池不再需要时,可以关闭线程池并释放资源。

线程池的好处包括:

  1. 降低线程创建和销毁的开销,节省系统资源。
  2. 控制并发线程数量,避免过多的线程竞争导致系统性能下降。
  3. 提供任务调度和执行的灵活性,可以根据需求动态调整线程池配置。
  4. 提供统一的线程管理和监控,方便调试和优化应用程序。

项目链接

https://github.com/Pithikos/C-Thread-Pool

API分析

这个项目使用C语言实现了最为简单的线程池技术,初学者也可以通过这个项目快速理解并使用到自己的线程池,下面是这个项目所提供的API,所有API的介绍都在:thpool.h 中

  1. threadpool thpool_init(int num_threads) 初始化线程池,返回一个包含有num_threads个线程的线程池。
  2. int thpool_add_work(threadpool, void (*function_p)(void*), void* arg_p); 添加工作(function_p)到线程工作队列中,由线程池中的线程进行调用。
  3. void thpool_wait(threadpool); 等待线程池中所有任务执行完成。
  4. void thpool_pause(threadpool); 暂停当前线程池中的所有线程。
  5. void thpool_resume(threadpool); 恢复暂停中的线程池中的所有任务。
  6. void thpool_destroy(threadpool); 摧毁当前线程池中所有任务。
  7. int thpool_num_threads_working(threadpool); 获取当前线程池中有多少正在工作的任务。

实现分析

1. 线程池初始化:thpool_init

该库声明了两个全局变量:

static volatile int threads_keepalive;
static volatile int threads_on_hold;

keepalive表示线程池正在工作的标志位,on_hold表示线程池挂起的标志位。

所以初始化,第一步是设置两个标志位:

threads_on_hold   = 0;
threads_keepalive = 1;

表示默认创建的线程池的状态应该是工作保持工作状态而不是先挂起。

然后申请线程池工作队列所需的资源,并初始化每线程池属性锁。

最后,根据我们传入的线程池的大小,申请对应的线程资源:函数:thread_init

线程初始化函数实现如下:

*thread_p = (struct thread*)malloc(sizeof(struct thread));
if (*thread_p == NULL){
	err("thread_init(): Could not allocate memory for thread\n");
	return -1;
}

(*thread_p)->thpool_p = thpool_p;
(*thread_p)->id       = id;

pthread_create(&(*thread_p)->pthread, NULL, (void * (*)(void *)) thread_do, (*thread_p));
pthread_detach((*thread_p)->pthread);

申请一个thread句柄,将thrad句柄赋值到thread数组中。
在这里插入图片描述
在初始化的过程中,我们需要改变线程数组中的内容,所以需要给线程初始化函数中传递一个指向该变量的指针,而不是当前变量。

2. 添加工作到线程池中:thpool_add_work

这个函数有三个参数:thpool_* thpool_p, void (*function_p)(void*), void* arg_p

指向线程池的指针
指向工作任务函数的函数指针
需要传递给工作任务的参数
函数实现流程如下:

//创建一个新的work
job* newjob;
// 申请该work所需空间
newjob=(struct job*)malloc(sizeof(struct job));
if (newjob==NULL){
	err("thpool_add_work(): Could not allocate memory for new job\n");
	return -1;
}
// 赋值 工作函数指针及参数到工作创建的work中
/* add function and argument */
newjob->function=function_p;
newjob->arg=arg_p;

/* add job to queue */
jobqueue_push(&thpool_p->jobqueue, newjob);

3. 关键实现 thread_do

该函数是线程池实现的关键,我们线程池中对于线程的执行及调度,实现过程如下:

设置线程可以接收的信号量:prctl(PR_SET_NAME, thread_name);

获取线程资源到当前函数中:thpool_* thpool_p = thread_p->thpool_p;

下面是核心实现逻辑:
//线程可以工作
while(threads_keepalive){

	bsem_wait(thpool_p->jobqueue.has_jobs);

	if (threads_keepalive){

//如果线程可以工作,则正在工作的线程数+1
pthread_mutex_lock(&thpool_p->thcount_lock);
thpool_p->num_threads_working++;
pthread_mutex_unlock(&thpool_p->thcount_lock);

		/* Read job from queue and execute it */
		void (*func_buff)(void*);
		void*  arg_buff;
		job* job_p = jobqueue_pull(&thpool_p->jobqueue);
		if (job_p) {

//这里是工作队列调度的核心,如果任务存在,且可以配执行,就将函数指针及其参数赋值到变量中进行调用执行

				func_buff = job_p->function;
				arg_buff  = job_p->arg;
				func_buff(arg_buff);
			        free(job_p);
			}
// 执行完成,正在工作的线程数 -1
			pthread_mutex_lock(&thpool_p->thcount_lock);
			thpool_p->num_threads_working--;
			if (!thpool_p->num_threads_working) {
				pthread_cond_signal(&thpool_p->threads_all_idle);
			}
			pthread_mutex_unlock(&thpool_p->thcount_lock);

		}
	}

这个函数只是一个最简单最基本的调用方式,根据我们添加的顺序来完成工作队列中任务的执行,在此基础上,我们可以增加线程池中工作的优先级、抢占的执行机制,这样对我们thread_do函数中的逻辑会产生比较大的逻辑变动,这个就由我们后面去进行分析了。

示例分析

#include <stdio.h>
#include <pthread.h>
#include <stdint.h>
#include "thpool.h"

void task(void *arg){
	printf("Thread #%u working on %d\n", (int)pthread_self(), (int) arg);
}


int main(){
	
	puts("Making threadpool with 4 threads");
	threadpool thpool = thpool_init(4);

	puts("Adding 40 tasks to threadpool");
	int i;
	for (i=0; i<40; i++){
		thpool_add_work(thpool, task, (void*)(uintptr_t)i);
	};

	thpool_wait(thpool);
	puts("Killing threadpool");
	thpool_destroy(thpool);
	thpool_destroy(thpool);
	
	return 0;
}

这个示例非常简单,结合上面的API说明就可以看懂。

首先创建了一个4个线程的线程池,添加40个任务到线程池中,然后等待线程池执行完毕,最后释放线程池。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值