实现一个线程池,必须了解线程池的基本概念。
g i t h u b github github地址:threadpool
什么是线程池
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。
线程池工作机制
-
在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。
-
一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。
-
线程池中的线程,在任务队列为空时,等待任务的到来,任务队列中有任务时,则依次获取任务来执行,任务队列需要同步。
为什么需要线程池
线程池为线程生命周期开销问题和资源不足问题提供了解决方案。
通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。
使用线程池的风险
虽然线程池是构建多线程应用程序的强大机制,但使用它并不是没有风险的。用线程池构建的应用程序容易遭受任何其它多线程应用程序容易遭受的所有并发风险,诸如同步错误和死锁,它还容易遭受特定于线程池的少数其它风险,诸如与池有关的死锁、资源不足和线程泄漏。
线程同步
L i n u x Linux Linux线程同步有多种方法:互斥量、信号量、条件变量等
创建一个线程池,可以将这些方法进行一个简单的封装,方便线程池调用
//locker.h
/*************************************************************************
> File Name: locker.h
> Author: ersheng
> Mail: ershengaaa@163.com
> Created Time: Sat 23 Feb 2019 10:47:14 PM CST
************************************************************************/
#ifndef _LOCKER_H
#define _LOCKER_H
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
/* 信号量类 */
class sem_locker {
private:
sem_t _sem;
public:
/* 初始化信号量 */
sem_locker() {
if (sem_init(&_sem, 0, 0)) {
printf("sem init error\n");
}
}
/* 销毁信号量 */
~sem_locker() {
sem_destroy(&_sem);
}
/* 等待信号量 */
bool wait_sem() {
return sem_wait(&_sem) == 0;
}
/* 添加信号量 */
bool add_sem() {
return sem_post(&_sem) == 0;
}
};
/* 互斥量类 */
class mutex_locker {
private:
pthread_mutex_t _mutex;
public:
/* 初始化互斥量 */
mutex_locker() {
if (pthread_mutex_init(&_mutex, NULL)) {
printf("mutex init error\n");
}
}
/* 销毁互斥量 */
~mutex_locker() {
pthread_mutex_destroy(&_mutex);
}
/* 上锁 */
bool mutex_lock() {
return pthread_mutex_lock(&_mutex) == 0;
}
/* 解锁 */
bool mutex_unlock() {
return pthread_mutex_unlock(&_mutex) == 0;
}
};
/* 条件变量类 */
class cond_locker {
private:
pthread_mutex_t _mutex;
pthread_cond_t _cond;
public:
/* 初始化条件变量与互斥量 */
cond_locker() {
if (pthread_mutex_init(&_mutex, NULL))
printf("mutex init error\n");
if (pthread_cond_init(&_cond, NULL)) {
pthread_mutex_destroy(&_mutex);
printf("cond init error\n");
}
}
/* 销毁条件变量与互斥量 */
~cond_locker() {
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
/* 等待条件变量 */
bool wait_cond() {
int ans = 0;
pthread_mutex_lock(&_mutex);
ans = pthread_cond_wait(&_cond, &_mutex);
pthread_mutex_unlock(&_mutex);
return ans == 0;
}
/* 唤醒等待条件变量的线程 */
bool get_signal() {
return pthread_cond_signal(&_cond) == 0;
}
/* 激活所有线程 */
bool cond_broadcast() {
return pthread_cond_broadcast(&_cond) == 0;
}
};
#endif
实现线程池
线程池中用到了互斥量和条件变量;并使用 q u e u e queue queue作为任务队列
/*************************************************************************
> File Name: pthreadpool.h
> Author: ersheng
> Mail: ershengaaa@163.com
> Created Time: Sat 23 Feb 2019 11:39:39 PM CST
************************************************************************/
#ifndef _PTHREAD_POOL_
#define _PTHREAD_POOL_
#include "locker.h"
#include <queue>
#include <stdio.h>
#include <exception>
#include <errno.h>
#include <pthread.h>
#include <iostream>
template <class T>
class threadpool {
private:
int thread_number; /* 线程池的线程数 */
pthread_t *all_threads; /* 线程数组 */
std::queue<T *> task_queue; /* 任务队列 */
mutex_locker queue_mutex_locker; /* 互斥锁 */
cond_locker queue_cond_locker; /* 条件变量 */
bool is_stop; /* 判断是否结束线程 */
public:
threadpool(int thread_number = 20);
~threadpool();
bool append_task(T *task);
void start();
void stop();
private:
/* 线程运行的函数 */
static void *worker(void *arg);
void run();
T *getTask();
};
/* 初始化线程池 */
template <class T>
threadpool<T>::threadpool(int thread_num):
thread_number(thread_num),
is_stop(false), all_threads(NULL)
{
if (thread_number <= 0)
printf("threadpool can't init because thread_number = 0\n");
all_threads = new pthread_t[thread_number];
if (all_threads == 0) {
printf("can't init threadpool because thread array can't new\n");
}
}
/* 销毁线程池 */
template <class T>
threadpool<T>::~threadpool() {
delete []all_threads;
stop();
}
/* 停止线程池 */
template <class T>
void threadpool<T>::stop() {
is_stop = true;
queue_cond_locker.cond_broadcast();
}
/* 开始线程池 */
template <class T>
void threadpool<T>::start() {
for (int i = 0; i < thread_number; ++i) {
if (pthread_create(all_threads + i, NULL, worker, this) != 0) {
/* 线程创建失败,则销毁所有已申请的资源并抛出异常 */
delete []all_threads;
throw std::exception();
}
if (pthread_detach(all_threads[i])) {
/* 将线程设置为脱离线程,失败则清除成功申请的资源并抛出异常 */
delete []all_threads;
throw std::exception();
}
}
}
/* 添加任务进入任务队列 */
template <class T>
bool threadpool<T>::append_task(T *task) {
/* 获取互斥锁 */
queue_mutex_locker.mutex_lock();
bool is_signal = task_queue.empty(); /* 判断任务队列是否为空 */
task_queue.push(task); /* 加入任务队列 */
queue_mutex_locker.mutex_unlock(); /* 释放互斥锁 */
/* 如果任务队列不为空,则唤醒等待条件变量的线程 */
if (is_signal) {
queue_cond_locker.get_signal();
}
return true;
}
/* 线程调用函数 */
template <class T>
void *threadpool<T>::worker(void *arg) {
threadpool *pool = (threadpool *)arg;
pool->run();
return pool;
}
/* 获取任务 */
template <class T>
T* threadpool<T>::getTask() {
T *task = NULL;
queue_mutex_locker.mutex_lock(); /* 获取互斥锁 */
/* 判断任务队列是否为空 */
if (!task_queue.empty()) {
/* 获取队头任务并执行 */
task = task_queue.front();
task_queue.pop();
}
queue_mutex_locker.mutex_unlock();
return task;
}
template <class T>
void threadpool<T>::run() {
while (!is_stop) {
/* 等待任务 */
T *task = getTask();
if (task == NULL)
queue_cond_locker.wait_cond();
else {
task->doit(); /* doit是T对象中的方法 */
delete task;
}
//for test
//printf("exit %ld\n", (unsigned long)pthread_self());
}
}
#endif
线程池测试
编写一个简单的任务类来进行测试:
/*************************************************************************
> File Name: test.cpp
> Author: ersheng
> Mail: ershengaaa@163.com
> Created Time: Mon 25 Feb 2019 05:48:44 PM CST
************************************************************************/
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include "pthreadpool.h"
class task {
private:
int number;
public:
task(int num) : number(num) {
}
~task() {}
void doit() {
printf("this is the %d task\n", number);
}
};
int main() {
task *tsk;
threadpool<task> pool(10);
for (int i = 0; i < 20; ++i) {
tsk = new task(i);
pool.append_task(tsk);
}
pool.start();
sleep(10);
printf("close the thread pool\n");
pool.stop();
return 0;
}
测试结果: