线程池原理及其代码实现(C和C++)【池式组件】

本文详细介绍了线程池的原理及C语言和C++的实现方式。通过生产者-消费者模型,展示了如何创建、管理和销毁线程池,以及如何在C和C++中实现线程的同步与通信。线程池通过互斥锁和条件变量管理等待队列和运行队列,实现任务调度。示例代码演示了线程池的创建、任务提交和销毁过程。
摘要由CSDN通过智能技术生成

线程有哪些接口

  • 创建线程
pthead_create(pthread_t tid, void *attr, void *func, void *arg)
			  //线程id	      线程属性   线程执行的内容  线程参数
  • 线程退出
void pthread_exit(void *retval)
						//线程退出状态
  • 阻塞等待线程退出
int pthread_join(pthread_t thread, void **retual)
success--0  fail--errno
  • 获取线程id
pthread_t pthread_self(void);
return : success--0 fail--
  • 线程分离
int pthread_detach(pthread_t thread)
success--0 fail--errno
  • 杀死线程
int pthread_cancel(pthread_t thread)
success--0  fail--errno
  • 比较线程号是否相等
int pthread_equal(pthread_t t1, pthread_t2)

线程和进程控制原语对比

进程线程
创建forkpthread_create
退出exitpthread_exit
等待waitpthread_join
杀死killpthread_cancel
获取idgetpidpthread_self

如何实现线程池

生产者-消费者模型

  • 产品:等待队列
  • 生产者:程序
  • 消费者:运行队列

使用互斥锁和条件锁共同实现
1、举例创建了含有100个线程的线程池,线程池中每个线程先给互斥锁加锁,随后等待条件锁,等待条件锁的原则是先释放互斥锁,如果争夺到了条件锁就会马上请求获取互斥锁
2、首先时主程序创建新的任务节点,跟着拿到互斥锁,将任务节点插入等待队列,跟着给条件锁发送信号,唤醒正在线程池中的线程来争夺条件锁,最后释放互斥锁
3、线程池中的线程进行争夺条件锁,争夺到的线程就获取到互斥锁,随后向等待队列中获取一个任务节点,并执行任务节点中的任务函数,随后释放互斥锁

线程池的对象有哪些

1、等待队列

  • 任务节点就是指在等待被调度执行的节点
typedef struct NWORKER{
	pthread_t id;
	int terminate;
	struct NTHREADPOOL *pool;
	struct NWORKER *next;
	struct NWORKER *prev;
}nworker;

2、运行队列

  • 运行队列是指正在执行的队列
typedef struct  NJOB{
	void (*func_job)(struct NJOB *job);
	void *user_data;
	
	struct NJOB *prev;
	struct NJOB *next;
}njob;

3、线程池管理节点

  • 调度运行队列和等待队列,并加锁解锁
typedef struct NTHREADTOOL{

	struct NWORKER *workers;
	struct NJOB *wait_jobs;

	pthread_cond_t cond;
	pthread_mutex_t mtx;
	
}nthreadpool_t;

线程池的创建

  • 在堆区创建,将新创建的节点加入等待队列
//num_thread ---需要创建执行点的数量
int thread_pool_create(nthreadpool_t *pool, int num_thread){

	/*对线程池所有结构体初始化*/
	//pool
	if(pool==NULL)return -1;
	if(num_thread<1) num_thread = 1;
	memset(pool, 0, sizeof(nthreadpool_t)); 			//清零,避免脏数据
	
	//cond
	pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
	memcpy(&pool->cond, &blank_cond, sizeof(pthread_cond_t));

	//metux
	pthread_mutex_t blank_mtx = PTHREAD_MUTEX_INITIALIZER; 	
	memcpy(&pool->mtx, &blank_mtx, sizeof(pthread_mutex_t)); 	
	
	//create
	//一个线程对应一个执行点
	int idx = 0;
	for(idx=0; idx<num_thread; idx++){
		nworker *worker = (nworker*)malloc(sizeof(nworker));
		if(worker == NULL){
			perror("malloc\n");
			return idx;
		}
		memset(worker, 0, sizeof(nworker));

		worker->pool = pool;

		int ret = pthread_create(&worker->id, NULL, thread_callback, worker);
		if(ret){
			perror("pthread_create");
			free(worker);
			return idx;
		}
		LL_ADD(worker, pool->workers);
	}
	return idx;
}

线程池的销毁

  • 将标志位设置为1,线程会在回调函数中判断标志位进行销毁,解锁并唤醒全部阻塞的线程
void thread_pool_destroy(nthreadpool_t *pool){
	nworker *worker = NULL;
	for(worker = pool->workers; worker != NULL; worker = worker->next){

		worker->terminate = 1;
		
	}
	pthread_mutex_lock(&pool->mtx);
	pthread_cond_broadcast(&pool->cond); //唤醒全部阻塞的线程
	pthread_mutex_unlock(&pool->mtx);
}

等待队列和运行队列之间的切换

  • 很简单,直接将等待队列中的节点加入到运行队列里面
void thread_pool_push_job(nthreadpool_t *pool, njob *job){
	
	pthread_mutex_lock(&pool->mtx);
	
	LL_ADD(job, pool->wait_jobs);		//加入任务队列
	pthread_cond_signal(&pool->cond); 	//向条件锁发送信号
	
	pthread_mutex_unlock(&pool->mtx);
}

线程的回调函数

  • 生产者和消费者模型,条件变量和互斥锁共同实现
void *thread_callback(void *arg){

	nworker *worker = (nworker*) arg;

	//取任务并执行
	while(1){
		//加互斥锁
		pthread_mutex_lock(&worker->pool->mtx);
		
		//判断任务队列是否为空
		while(worker->pool->wait_jobs == NULL){
			if(worker->terminate)break;
			//条件等待唤醒
			pthread_cond_wait(&worker->pool->cond, &worker->pool->mtx);
		}
		if(worker->terminate){
			pthread_mutex_unlock(&worker->pool->mtx);
			break;
		}
		
		//从等待队列取任务,取了就删除
		njob *job = worker->pool->wait_jobs;
		if(job){
			LL_REMOVE(job, worker->pool->wait_jobs);
		}
		
		//解互斥锁
		pthread_mutex_unlock(&worker->pool->mtx);

		if(job == NULL)continue; //	为何为空,多个线程竞争一个任务,会出现有的线程竞争到空的资源,避免死锁则加入判断跳出

		//执行任务内容
		job->job_func(job);
	}
	free(worker);

运行示例

  • 开一百个线程,并打印自身线程id
#if 1

void  counter(njob *job){

	if(job==NULL)return;

	int idx = *(int*)job->user_data;
	printf("[%lu]idx=%d,\n", pthread_self(), idx);

	free(job->user_data);
	free(job);
}

#define MAX_COUNT 		1000000

int main(){

	nthreadpool_t pool = {0};

	int num_thread = 100;

	thread_pool_create(&pool, num_thread);

	int i=0;
	for(i=0; i<MAX_COUNT; i++){
		//创建job
		njob *job = (njob*)malloc(sizeof(njob));
		if(job==NULL) exit(1);
		job->job_func = counter;
		job->user_data = malloc(sizeof(int));  		//定义在堆上,防止栈上系统自动回收
		*(int*)job->user_data = i;
		//将job加入任务队列
		thread_pool_push_job(&pool,job);
	}
	getchar();
	return 0;
}

#endif

C++实现的线程池类

  • 原理和C的一摸一样,不过等到队列用的是数组,调度的时候并不像C那样会自动从等待队列将节点放入运行队列
#ifndef _THREADPOOL_H
#define _THREADPOOL_H


#include <list>
#include "locker.h"
#include <pthread.h>
#include <execption>
#include <cstdio>

//线程池类
template<typename T>
class threadpool{
public:
	threadpool(int thread_number = 8, int max_requests = 10000);

	~threadpool();		

	bool apend(T *request);		//将request加入工作队列
	
private:
	int m_thread_pool_number;	//线程池的数量

	pthread_t *m_threads;			//线程数组

	int m_max_request;

	std::list<T*> m_workqueue;   //工作队列

	mutex m_queuemutex;		//线程安全
	cond m_queuecond;
	sem m_queuesem;

	bool m_stop;
	
private:
	static void* worker(void *arg);
	void run();
};


template<typename T>
threadpool<T>::threadpool(int thread_number, int max_requests):
	m_thread_pool_number(thread_number), m_max_request(max_requests), 
	m_stop(NULL), m_threads(NULL){

	if((thread_number<=0) || (m_max_request<=0)){
		throw std::execption();
	}

	m_threads = new pthread_t[m_thread_number];
	if(!m_threads){
		throw std::execption();
	}
	
	this->m_queuemutex = new mutex();
	this->m_queuecond = new cond();
	this->m_queuesem = new sem();

	for(int i=0; i<thread_number; ++i){
		 printf("create the %dth thread\n", i);
		if(pthread_create(m_threads + i, NULL, worker, this)!=0){
			delete [] m_threads;
			throw std::execption();
		}
		if(pthread_detach(m_thread[i]){
			throw std::execption();
		}
	}
}

template<typename T>
threadpool<T>::~threadpool(){
	delete [] m_threads;
	delete m_queuemutex;
	delete m_queuecond;
	delete m_queuesem;
	m_stop  = true;
}

template<typename T>
bool threadpool<T>::append(T *request){
	m_queuemutex.lock();
	if(m_workqueue.size() > m_max_request){
		m_queuemutex.unlock();
		return false;
	}
	m_workqueue.push_back(request);
	m_queuemutex.unlock();
	m_queuesem.post();
	return true;
}

template<typename T>
void* threadpool<T>::worker(void* arg){
	threadpool * pool = (threadpool*)arg;

	pool->run();
	return pool;
}

template<typename T>
void threadpool<T>::run(){
	while(!m_stop){
		m_queuesem.wait();
		m_queuemutex.lock();
		if(m_workqueue.empty()){
			m_queuemutex.unlock();
			continue;
		}
		T* request = m_workqueue.front();
		m_workqueue.pop_back();
		m_queuemutex.unlock();
		if(!request){
			continue;
		}
		request->process();
	}
}

#endif
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fallsom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值