为啥需要线程池?
什么情况可以用:
我们经常会遇到一个队列,其中的消息依赖大量的耗时操作,一个线程从队列拉取完全处理不过来,这时候,就需要我们引入线程池的技术。
什么任务队列不适合用
如果队列的消息是有状态的,不建议用,因为多线程的任务分发是随机的,如果你的任务依赖于前一次处理的结果或者对时序有影响,请从整个系统的设计重新考虑任务队列的设计,将不同业务的数据分配到不同的任务队列,来实现对耗时队列的处理,而不是将有状态的任务到多线程处理,否则不可避免要面临数据共享的多线程竞争问题。
线程池如何设计?
1、线程池需要涉及的数据结构接口
工作线程:其中维持了池结构的成员指针,工作worker线程需要通过池成员指针,来获取任务队列的任务。
class CWorker
{
public:
CWorker(): m_nExit(0)
{}
virtual ~CWorker() {}
static void* WorkerCallBack(void * p);
public:
int m_nExit;
pthread_t m_PID;
CThreadPool* m_pPool;
};
任务结构,虚函数RunJob,根据实际情况派生该结构并实现对应的任务队列实现处理,可自定义自己的任务队列结构。
class CJobs
{
public:
CJobs() {};
virtual ~CJobs() {};
virtual void RunJobs() = 0;
static void JobsCallback(CJobs* pJobs)
{
pJobs->RunJobs();
}
};
线程池,其中包括任务队列,处理工作任务的worker,因为多个线程同时取任务队列,需要一个互斥锁,条件变量来唤醒挂起的线程处理数据
class CThreadPool
{
public:
CThreadPool();
virtual ~CThreadPool();
int CreateThreadPool(int nWorkerNums);
int PushJobs(CJobs* pJobs);
int RemoverJobs(CJobs* pJobs);
list<CWorker*> m_Workers;
list<CJobs*> m_Jobs;
pthread_mutex_t m_Mutex;
pthread_cond_t m_Cond;
};
2、接口实现:
创建线程:
int CThreadPool::CreateThreadPool(int nWorkerNums)
{
pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
memcpy(&m_Cond, &blank_cond, sizeof(m_Cond));
pthread_mutex_t blank_mutext = PTHREAD_MUTEX_INITIALIZER;
memcpy(&m_Mutex, &blank_mutext, sizeof(blank_mutext));
for(int i=0; i<nWorkerNums; i++)
{
CWorker *worker = new CWorker();
if (worker == NULL) {
assert(0);
return 1;
}
int ret = pthread_create(&worker->m_PID, NULL, &worker->WorkerCallBack, (void *)worker);
if (ret) {
assert(0);
delete worker;
return 1;
}
worker->m_pPool = this;
m_Workers.push_back(worker);
}
}
压入队列:
int CThreadPool::PushJobs(CJobs* pJobs)
{
pthread_mutex_lock(&m_Mutex);
m_Jobs.push_back(pJobs);
pthread_mutex_unlock(&m_Mutex);
pthread_cond_signal(&m_Cond);
}
线程回调:
void* CWorker::WorkerCallBack(void * p)
{
class CWorker* pWorker = (CWorker*)p;
while (!pWorker->m_nExit)
{
pthread_mutex_lock(&pWorker->m_pPool->m_Mutex);
while (pWorker->m_pPool->m_Jobs.empty())
{
if(pWorker->m_nExit)
break;
pthread_cond_wait(&pWorker->m_pPool->m_Cond, &pWorker->m_pPool->m_Mutex);
}
CJobs* pJobs = pWorker->m_pPool->m_Jobs.front();
pWorker->m_pPool->m_Jobs.pop_front();
pthread_mutex_unlock(&pWorker->m_pPool->m_Mutex);
pJobs->JobsCallback(pJobs);
}
}
demo的简单测试:
class CMyjobs :public CJobs
{
private:
/* data */
public:
CMyjobs() {}
virtual ~CMyjobs() {}
virtual void RunJobs()
{
printf("you are right!\n");
}
};
int main()
{
CThreadPool pool;
pool.CreateThreadPool(10);
CMyjobs myJobs1;
while (1)
{
printf("push\n");
pool.PushJobs(&myJobs1);
usleep(100);
}
return 0;
}
如果对线程要求高,可以加入无锁队列来实现更加高效的线程池。
线分享到这里。这是最简单版本的线程池实现。