线程池的设计
线程池,顾名思义就是一个存放了已经创建好的线程资源的池子,当有任务提交给线程池的时候,池中的某一个线程就会执行该任务,执行完毕之后就会回到池子中等待下次执行任务。
从上图中我们可以看到,实现一个线程池应该具备下列要素:
- 线程队列:提交的任务缓冲在这里
- 线程数量管理功能
- 任务拒绝策略
- 队列长度
线程安全队列的封装
线程池的定义
class YR_ThreadPool : public YR_ThreadLock
{
public:
YR_ThreadPool();
virtual ~YR_ThreadPool();
//线程池的初始化
void init(int num);
//启动所有线程
void start();
//终止线程池
void terminate();
//获取线程数量
size_t getThreadNum() const;
//获取线程池的任务数量
size_t getThreadJobNum() const;
//添加任务
bool Execute(std::function<void()> tf);
//清除线程池
void clear();
//通知等待在任务队列的线程醒来
void notifyT();
//等待所有工作全部结束
//millsecond表示等待的时间
bool waitForAllDone(int millsecond = -1);
public:
//线程中的工作线程
class YR_ThreadWorker : public YR_Thread
{
public:
YR_ThreadWorker(YR_ThreadPool *pool);
~YR_ThreadWorker();
virtual void run(); //实现逻辑
void terminate(); //线程终止
private:
YR_ThreadPool* _tpool;
bool _bTerminate; //是否结束线程
};
private:
YR_ThreadQueue<std::function<void()>> _jobqueue; //任务队列
YR_ThreadLock _tmutex; //任务队列的锁
std::vector<YR_ThreadWorker*> _jobthread; //工作队列
std::set<YR_ThreadWorker*> _busthread; //繁忙线程
bool _bAllDone; //是否所有任务执行完毕
};
线程池的实现
构造与析构
YR_ThreadPool::YR_ThreadPool()
:bAllDone(true)
{}
YR_ThreadPool::~YR_ThreadPool()
{
terminate();
clear();
}
线程池的初始化
void YR_ThreadPool::init(size_t num)
{
terminate();
Lock sync(*this);
clear();
for(size_t i=0; i<num; i++)
{
_jobthread.push_back(new ThreadWorker(this));
}
}
启动所有线程
void YR_ThreadPool::start()
{
Lock sync(*this);
auto it = _jobthread.begin();
while(it != _jobthread.end())
{
(*it)->start();
++it;
}
_bAllDone = false;
}
终止线程池
void YR_ThreadPool::terminate()
{
Lock sync(*this);
auto it = _jobthread.begin();
while(it != _jobthread.end())
{
if((*it)->isAlive())
{
(*it)->terminate();
(*it)->getThreadControl.join(); //线程回收
}
++it;
}
}
获取线程数数量
size_t YR_ThreadPool::getThreadNum()
{
Lock sync(*this);
return _jobthread.size();
}
获取线程池的任务数量
size_t YR_ThreadPool::getThreadJobNum()
{
return _jobqueue.size();
}
添加任务
void YR_ThreadPool::Execute(std::function<void()> tf)
{
_jobqueue.push_back(std::move(tf));
}
清除线程池
void YR_ThreadPool::clear()
{
auto it = _jobthread.begin();
while(it != _jobthread.end())
{
delete(*it);
++it;
}
_jobthread.clear();
_busthread.clear();
}
通知等待在工作队列的线程醒来
void YR_ThreadPool::notifyT()
{
_jobqueue.notifyT();
}
线程运行逻辑
void YR_ThreadPool::ThreadWorker::run()
{
//调用初始化部分
auto pst = _tpool->get();
if(pst)
{
try
{
pst();
}
catch ( ... )
{
}
}
//调用处理部分
while (!_bTerminate)
{
auto pfw = _tpool->get(this);
if(pfw)
{
try
{
pfw();
}
catch ( ... )
{
}
_tpool->idle(this);
}
}
//结束
_tpool->exit();
}
工作线程终止
void YR_ThreadPool::ThreadWorker::terminate()
{
_bTerminate = true;
_tpool->notifyT();
}
等待所有工作全部结束
bool YR_ThreadPool::waitForAllDone(int millsecond)
{
Lock sync(_tmutex);
start1:
//任务队列和繁忙线程都是空的
if (finish())
{
return true;
}
//永远等待
if(millsecond < 0)
{
_tmutex.timedWait(1000);
goto start1;
}
int64_t iNow= YR_Common::nowToms();
int m = millsecond;
start2:
bool b = _tmutex.timedWait(millsecond);
//完成处理了
if(finish())
{
return true;
}
if(!b)
{
return false;
}
millsecond = max((int64_t)0, m - (YR_Common::nowToms() - iNow));
goto start2;
return false;
}
线程存储
“防止任务在共享资源上发生冲突的第二种方式是根除对变量的共享”。线程局部存储为每一个访问此变量的线程提供一个此变量独立的副本,线程可以修改此变量,而不会影响到其他线程。
线程私有数据的封装
class ThreadData
{
public:
ThreadData(){}
virtual ThreadData(){}
template<class T>
static T* makeThreadData(){ return new T;}
};
YR_ThreadPool
类中添加pthread_key_t g_key
属性。
YR_ThreadPool
类中添加static void destructor(void *p);
方法,用来销毁线程数据。
线程池中添加线程私有数据的操作方法
线程存储的操作方法
#include <pthread.h>
// Returns 0 on success, or a positive error number on error
int pthread_key_create (pthread_key_t *key, void (*destructor)(void *));
// Returns 0 on success, or a positive error number on error
int pthread_key_delete (pthread_key_t key);
// Returns 0 on success, or a positive error number on error
int pthread_setspecific (pthread_key_t key, const void *value);
//该函数将value的值(不是内容)与key相关联。用pthread_setspecific为一个键指定新的线程数据时,线程必须先释放原有的线程数据用以回收空间。
// Returns pointer, or NULL if no thread-specific data is associated with key
void *pthread_getspecific (pthread_key_t key);
原理参考:linux线程私有数据详解
static void setThreadData(ThreadData *p)
{
//释放原有的线程数据
YR_ThreadPool::ThreadData *pOld = getThreadData();
if(pOld != NULL && pOld != p)
delete pOld;
int ret = pthread_setspecific(g_key, (void*)p);
if(ret != 0)
{
throw YR_ThreadPool_Exception("[YR_ThreadPool::setThreadData] pthread_setspecific error",ret);
}
}
static void setThreadData(pthread_key_t pkey, ThreadData *p)
{
//释放原有的线程数据
YR_ThreadPool::ThreadData *pOld = getThreadData(pkey);
if(pOld != NULL && pOld != p)
delete pOld;
int ret = pthread_setspecific(pkey, (void *)p);
if(ret != 0)
{
throw YR_ThreadPool_Exception("[YR_ThreadPool::setThreadData] pthread_setspecific error", ret);
}
}
static ThreadData* getThreadData()
{
return (ThreadData*)pthread_getspecific(g_key);
}
static ThreadData* getThreadData(pthread_key_t pkey)
{
return (ThreadData*)pthread_getspecific(pkey);
}
//析构函数
static void destructor(void *p)
{
ThreadData* ttd = (ThreadData*)p;
delete ttd;
}
key的初始化
class KeyInitialize
{
public:
//初始化key
KeyInitialize()
{
int ret = pthread_key_create(&YR_ThreadPool::g_key, YR_ThreadPool::destructor);
if(ret != 0)
{
throw YR_ThreadPool_Exception("[YR_ThreadPool::KeyInitialize] pthread_key_create error", ret);
}
}
//释放key
~KeyInitialize()
{
pthread_key_delete(YR_ThreadPool::g_key);
}
};
YR_ThreadPool
添加static KeyInitialize g_key_initialize
属性。
测试
#include <iostream>
#include "util/YR_ThreadPool.h"
using namespace youren;
using namespace std;
int main()
{
YR_ThreadPool tpool;
tpool.init(4);
tpool.start();
std::set<pthread_t> threadIds{};
for (int i = 0; i < 100000; i++)
{
tpool.exec([i, &threadIds]
{
cout << "index = " << i << ", current thread id : " << pthread_self() << endl;
threadIds.insert(pthread_self());
});
}
bool b = tpool.waitForAllDone(-1);
cout << "waitForAllDone..." << b << ":" << tpool.getJobNum() << endl;
tpool.stop();
cout << "执行任务的线程id : " << endl;
for(const auto& iter: threadIds)
{
cout << iter << endl;
}
return 0;
}
运行结果
......
index = 99998, current thread id : 140147597563648
index = 99999, current thread id : 140147597563648
index = 97378, current thread id : 140147605956352
97376, current thread id : 140147589170944
waitForAllDone...1:0
执行任务的线程id :
140147589170944
140147597563648
140147605956352
140147614349056