线程池的原理与实现
讲解内容
这两天重点学习了线程池这种基础组件,并且自己试着手写了一份线程池的代码,在这里与大家分享一下学习成果。
1.线程池的作用
1.1线程池的优点
线程池是帮助我们管理线程的工具,它维护了多个线程,可以降低资源的消耗,提高系统的性能。并且通过使用线程池,我们开发人员可以更好的把精力放在任务代码上,而不去管线程是如何执行的,实现任务提交和执行的解耦。
1.2线程池在什么地方使用
假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。 如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
2.线程池的工作原理
线程池主要由三个部分组:在调度器,任务队列,工作队列。线程池最开始只需要创建若干个线程并将他们以队列的形式放在工作队列之中,并且用条件变量监视任务队列,如果任务队列为空则阻塞,这样的话在所有线程创建完成后就会全部阻塞在等待任务到来的地方。之后由程序员编写任务并加入任务队列之中,就能唤醒阻塞线程,完成任务。
3.线程池的实现
#include<queue>
#include<pthread.h>
#include<stdlib.h>
using namespace std;
class njobs;
class threadpool;
typedef void NJOBCALLBACK(njobs *job); //回调函数
class njobs
{
public:
njobs();
NJOBCALLBACK *m_job_function;
void *m_user_data; //任务带的数据
};
class nworkers
{
public:
nworkers();
~nworkers();
pthread_t m_thread;
int m_terminate; //是否终止
};
class threadpool
{
public:
threadpool();
int ThreadPoolCreate(int max_thread); //创建线程
int ThreadPoolQueue(njobs *job);//任务入队
static void *WorkerThread(void *ptr);
private:
int m_max_thread;
queue<njobs> m_waiting_jobs;//任务等待队列
queue<nworkers> m_workers;//所包含的初始线程
pthread_mutex_t m_jobs_mtx;
pthread_cond_t m_jobs_cond;
};
//pthread_create传入的参数
struct workaddjob
{
nworkers *worker;
threadpool *pool;
};
```cpp
#include "threadpool.h"
#include<stdio.h>
njobs::njobs()
{
m_job_function = NULL;
m_user_data = NULL;
}
nworkers::nworkers()
{
m_terminate=0;
}
nworkers::~nworkers()
{
pthread_exit(NULL);
}
threadpool::threadpool()
{
m_max_thread = 0;
pthread_mutex_init(&m_jobs_mtx, NULL);
pthread_cond_init(&m_jobs_cond, NULL);
}
void* threadpool::WorkerThread(void *ptr)
{
workaddjob *wb = (workaddjob *)ptr;
nworkers *curworker = wb->worker;
threadpool *curpool = wb->pool;
while(1)
{
//pthread_cond_wait前必须加锁
pthread_mutex_lock(&curpool->m_jobs_mtx);
while(curpool->m_waiting_jobs.empty())
{
if(curworker->m_terminate) break;
//函数最终会阻塞在此处,pthread_cond_wait会先解锁,之后在唤醒时候再上锁。
pthread_cond_wait(&curpool->m_jobs_cond,&curpool->m_jobs_mtx);
}
if(curworker->m_terminate)
{
pthread_mutex_unlock(&curpool->m_jobs_mtx);
break;
}
njobs job = curpool->m_waiting_jobs.front();
curpool->m_waiting_jobs.pop();
pthread_mutex_unlock(&curpool->m_jobs_mtx);
job.m_job_function(&job);
}
}
int threadpool::ThreadPoolCreate(int max_thread)
{
if(max_thread < 1) max_thread = 1;
else m_max_thread=max_thread;
//循环创建线程
for(int i = 0;i < m_max_thread;i++)
{
nworkers *worker = new nworkers;
if (worker == NULL)
{
perror("malloc");
return 1;
}
workaddjob *wb=new workaddjob;
wb->pool=this;
wb->worker =worker;
int ret = pthread_create(&worker->m_thread, NULL, this->WorkerThread, (void *)wb);
if (ret)
{
perror("pthread_create");
delete worker;
return 1;
}
m_workers.push(*worker);
}
return 0;
}
//任务入队
int threadpool::ThreadPoolQueue(njobs *job)
{
if(job == NULL)
{
perror("job add");
return 1;
}
pthread_mutex_lock(&m_jobs_mtx);
m_waiting_jobs.push(*job);
pthread_cond_signal(&m_jobs_cond);
pthread_mutex_unlock(&m_jobs_mtx);
return 0;
}
//以下是测试案例
```cpp
#include <iostream>
#include<threadpool.h>
using namespace std;
#define MAX_THREAD 80
#define COUNTER_SIZE 1000
void counter(njobs *job) {
int *index = reinterpret_cast<int *>(job->m_user_data);
printf("index : %d, selfid : %lu\n", index, pthread_self());
}
int main(int argc, char *argv[]) {
threadpool pool;
pool.ThreadPoolCreate(MAX_THREAD);
int i = 0;
for (i = 0;i < COUNTER_SIZE;i ++) {
njobs *job = (njobs*)malloc(sizeof(njobs));
if (job == NULL)
{
perror("malloc");
exit(1);
}
job->m_job_function = counter;
job->m_user_data =reinterpret_cast<void *>(i);
pool.ThreadPoolQueue(job);
}
printf("finish\n");
getchar();
return 0;
}
内容就是不断创建新的线程并且将数字传进去接下来让我们看看结果
可见线程池的运行是没有顺序的。