C++实现线程池。

欢迎转载,转载请注明原出处:http://blog.csdn.net/ithzhang/article/details/9020283

代码地址:https://github.com/ithzhang/ThreadpoolLib.git

本文介绍的线程池采用C++语言,在windows平台下实现。此版本为Version 1.0,以后还会推出功能更完备的后续版本。本着技术分享的精神写作本文同时公布源代码。欢迎大家指出该线程池存在的问题并对当前性能进行讨论。

适用场景:

1.需要大量的线程来完成任务,且完成任务的时间比较短。

2.对性能要求苛刻的应用,比如要求服务器迅速相应客户请求。

3.接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。

不适合在以下场景下使用:

1.可能会长时间运行的任务。

2.具有良好的优先级控制。(本线程池仅仅实现了简单的优先级控制,有两种优先级:普通级和高级)。

使用到的数据结构:

   任务队列:任务缓冲区,用于存储要执行任务的队列。可以调用线程池成员函数向该队列中增加任务。

   空闲线程堆栈:用于存储空闲线程。空闲线程堆栈中会被压入指定数量的线程类对象指针。线程对象个数等于创建线程时初始线程个数。

   活动线程链表:用以存储当前正在执行任务的线程。当有任务到来时,线程会从空闲堆栈转移到活动链表中。任务完成,且任务队列中没有任务时,会从活动链表转移到空闲堆栈中。本文中我称其为线程状态转换。

调度机制:

1.向任务队列添加任务后,会检查此时空闲线程堆栈中是否有空闲线程,如有则从任务队列队首取出任务执行。

2.当线程执行完当前任务,准备转移到空闲堆栈时,也会检查当前任务队列是否为空。若不为空,则继续取出任务执行。否则,转换到空闲线程堆栈。

除上述两种调度机制外,没有采用其他机制。

在创建线程池时会指定一个初始线程个数。此处我采取的是:一次性创建用户指定的线程,并加入到空闲线程堆栈。以后这个数量无法更改,且不会随着任务的多寡而增添或减少。

所有处于空闲队列中的线程都由于等待事件对象触发而处于阻塞态。等待事件对象成功的线程会进入到活动线程链表中。

使用到的类:

  CTask类:任务基类。每个任务应继承自此类,并实现taskProc成员函数。

  CMyThread类:工作线程类。每个类管理一个线程。同时关联一个任务类对象。

  CThreadPool类:线程池类,用以创建并管理线程池,同时实现对线程池内线程的调度。

  CMyStack类:空闲线程堆栈,用以存储空闲的工作线程。

  CMyList类:活动线程队列。用以存储目前正在执行任务的线程。

  CTaskQueue类:任务队列。用以存储要执行的任务。

  CMyMutex类:互斥类。用于实现线程互斥访问。CMyStack,CMyList和CMyQueue内部都使用了CMyMutex类。它们是线程安全的。

MyThread类和CThreadPool类为核心类。其余为辅助类。

CTask类

CTask是任务基类,所以非常简单,仅仅提供接口。

其声明如下:

  1. class CTask
  2. {
  3. public:
  4. CTask(int id);
  5. ~CTask(void);
  6. public:
  7. virtual void taskProc()=0;
  8. bool getID();
  9. private:
  10. int m_ID;
  11. };
class CTask
{
public:
CTask(int id);
~CTask(void);
public:
virtual void taskProc()=0;
bool getID();
private:
int m_ID;
};

具体的任务类应继承自此基类,并实现taskProc函数。在该函数实现需要线程池执行的任务。

如:

  1. //TestTask.h
  2. #include "task.h"
  3. class CTestTask :
  4. public CTask
  5. {
  6. public:
  7. CTestTask(int id);
  8. ~CTestTask(void);
  9. public:
  10. virtual void taskProc();
  11. };
  12. //TestTask.cpp
  13. #include "TestTask.h"
  14. CTestTask::CTestTask(int id)
  15. :CTask(id)
  16. {
  17. }
  18. CTestTask::~CTestTask(void)
  19. {
  20. }
  21. void CTestTask::taskProc()
  22. {
  23. //模拟任务。
  24. for(int i=0;i<10000;i++)
  25. {
  26. for(int j=0;j<10000;j++)
  27. {
  28. int temp=1;
  29. temp++;
  30. }
  31. }
  32. }
//TestTask.h
#include "task.h"
class CTestTask :
public CTask
{
public:
CTestTask(int id);
~CTestTask(void);
public:
virtual void taskProc();
};
//TestTask.cpp
#include "TestTask.h"
CTestTask::CTestTask(int id)
:CTask(id)
{
}
CTestTask::~CTestTask(void)
{
}
void CTestTask::taskProc()
{
//模拟任务。
for(int i=0;i<10000;i++)
{
for(int j=0;j<10000;j++)
{
int temp=1;
temp++;
}
}
}

CMyStack空闲线程堆栈类

CMyStack类用以存储空闲线程。内部采用stack实现。之所以采用栈来存储线程类对象,是因为:当一个线程执行完任务后,如果此时任务队列没有新任务,该线程就被压入到空闲线程栈。此后当有新任务到来时,栈顶元素,也就是刚刚被压入的线程会被弹出执行新任务。由于该线程是最近才被压入,其对应内存空间位于内存中的概率比其他线程的概率要大。这在一定程度上可以节省从系统页交换文件交换到物理内存的开销。

  1. //MyStack.h
  2. #pragma once
  3. #include<stack>
  4. #include "MyMutex.h"
  5. class CMyThread ;
  6. class CMyStack
  7. {
  8. public:
  9. CMyStack(void);
  10. ~CMyStack(void);
  11. public:
  12. CMyThread* pop();
  13. bool push(CMyThread*);
  14. int getSize();
  15. bool isEmpty();
  16. bool clear();
  17. private:
  18. std::stack<CMyThread*> m_stack;
  19. CMyMutex m_mutext;
  20. };
//MyStack.h
#pragma once
#include<stack>
#include "MyMutex.h"
class CMyThread ;
class CMyStack
{
public:
CMyStack(void);
~CMyStack(void);
public:
CMyThread* pop();
bool push(CMyThread*);
int getSize();
bool isEmpty();
bool clear();
private:
std::stack<CMyThread*> m_stack;
CMyMutex m_mutext;
};

CMyList活动线程链表类

CMyList类用以存储正在执行任务的线程。内部采用list实现。活动线程在执行完任务后,可以被随时从活动链表中删除。之所以使用链表是因为在链表中删除某一元素的开销很小。

  1. //MyList.h
  2. #pragma once
  3. #include <list>
  4. #include "MyMutex.h"
  5. class CMyThread;
  6. class CMyList
  7. {
  8. public:
  9. CMyList(void);
  10. ~CMyList(void);
  11. public:
  12. bool addThread(CMyThread*t);
  13. bool removeThread(CMyThread*t);
  14. int getSize();
  15. bool isEmpty();
  16. bool clear();
  17. private:
  18. std::list<CMyThread*>m_list;
  19. CMyMutex m_mutex;
  20. };
//MyList.h
#pragma once
#include <list>
#include "MyMutex.h"
class CMyThread;
class CMyList
{
public:
CMyList(void);
~CMyList(void);
public:
bool addThread(CMyThread*t);
bool removeThread(CMyThread*t);
int getSize();
bool isEmpty();
bool clear();
private:
std::list<CMyThread*>m_list;
CMyMutex m_mutex;
};

CMyQueue任务队列类

CMyQueue用以存储要执行的任务。内部采用双向队列实现。具有简单的优先级控制机制。当普通的优先级任务到来时,会正常入队。当高优先级任务到来时会插入到对首。线程池在调度时会简单的从队首取出任务并执行。

  1. //MyQueue.h
  2. #pragma once
  3. #include<deque>
  4. #include"MyMutex.h"
  5. class CTask;
  6. class CMyQueue
  7. {
  8. public:
  9. CMyQueue(void);
  10. ~CMyQueue(void);
  11. public:
  12. CTask*pop();
  13. bool push(CTask*t);
  14. bool pushFront(CTask*t);、
  15. bool isEmpty();
  16. bool clear();
  17. private:
  18. std::deque<CTask*>m_TaskQueue;
  19. CMyMutex m_mutex;
  20. };
//MyQueue.h
#pragma once
#include<deque>
#include"MyMutex.h"
class CTask;
class CMyQueue
{
public:
CMyQueue(void);
~CMyQueue(void);
public:
CTask*pop();
bool push(CTask*t);
bool pushFront(CTask*t);、
bool isEmpty();
bool clear();
private:
std::deque<CTask*>m_TaskQueue;
CMyMutex m_mutex;
};

CMyMutex互斥类

CMyMutex类用于控制线程互斥访问。内部采用CRITICAL_SECTION实现 。在对活动线程链表、空闲线程堆栈、任务队列进行访问时都需要进行互斥访问控制。防止多线程同时访问导致的状态不一致的情况出现。

类声明如下:

  1. //MyMutex.h
  2. #pragma once
  3. #include "windows.h"
  4. class CMyMutex
  5. {
  6. public:
  7. CMyMutex(void);
  8. ~CMyMutex(void);
  9. public:
  10. bool Lock();
  11. bool Unlock();
  12. private:
  13. CRITICAL_SECTION m_cs;
  14. };
//MyMutex.h
#pragma once
#include "windows.h"
class CMyMutex
{
public:
CMyMutex(void);
~CMyMutex(void);
public:
bool Lock();
bool Unlock();
private:
CRITICAL_SECTION m_cs;
};

CMyThread工作线程类

CMyThread类用于管理一个线程。该类内部有一个CTask*成员和一个事件对象。CTask*成员为与该线程关联的任务。调用assignTask可以为该线程设置对应的任务。

类声明如下:

  1. //MyThread.h
  2. #pragma once
  3. #include "windows.h"
  4. class CTask;
  5. class CBaseThreadPool;
  6. class CMyThread
  7. {
  8. public:
  9. CMyThread(CBaseThreadPool*threadPool);
  10. ~CMyThread(void);
  11. public:
  12. bool startThread();
  13. bool suspendThread();
  14. bool resumeThread();
  15. bool assignTask(CTask*pTask);
  16. bool startTask();
  17. static DWORD WINAPI threadProc(LPVOID pParam);
  18. DWORD m_threadID;
  19. HANDLE m_hThread;
  20. private:
  21. HANDLE m_hEvent;
  22. CTask*m_pTask;
  23. CBaseThreadPool*m_pThreadPool;
  24. };
//MyThread.h
#pragma once
#include "windows.h"
class CTask;
class CBaseThreadPool;
class CMyThread
{
public:
CMyThread(CBaseThreadPool*threadPool);
~CMyThread(void);
public:
bool startThread();
bool suspendThread();
bool resumeThread();
bool assignTask(CTask*pTask);
bool startTask();
static DWORD WINAPI threadProc(LPVOID pParam);
DWORD m_threadID;
HANDLE m_hThread;
private:
HANDLE m_hEvent;
CTask*m_pTask;
CBaseThreadPool*m_pThreadPool;
};

startThread用于创建入口函数为threadProc的线程。在该线程内部会循环等待一个事件对象。当没有任务到来时,线程就会在该事件对象上挂起。当新任务到来,线程池会将该线程对应的事件对象触发,然后执行其对应的任务。

  1. DWORD WINAPI CMyThread::threadProc( LPVOID pParam )
  2. {
  3. CMyThread *pThread=(CMyThread*)pParam;
  4. while(!pThread->m_bIsExit)
  5. {
  6. DWORD ret=WaitForSingleObject(pThread->m_hEvent,INFINITE);
  7. if(ret==WAIT_OBJECT_0)
  8. {
  9. if(pThread->m_pTask)
  10. {
  11. pThread->m_pTask->taskProc();、
  12. delete pThread->m_pTask;
  13. pThread->m_pTask=NULL;
  14. pThread->m_pThreadPool->SwitchActiveThread(pThread);
  15. }
  16. }
  17. }
  18. return 0;
  19. }
DWORD WINAPI CMyThread::threadProc( LPVOID pParam )
{
CMyThread *pThread=(CMyThread*)pParam;
while(!pThread->m_bIsExit)
{
DWORD ret=WaitForSingleObject(pThread->m_hEvent,INFINITE);
if(ret==WAIT_OBJECT_0)
{
if(pThread->m_pTask)
{
pThread->m_pTask->taskProc();、
delete pThread->m_pTask;
pThread->m_pTask=NULL;
pThread->m_pThreadPool->SwitchActiveThread(pThread);
}
}
}
return 0;
}

当任务执行完之后,线程内部会调用线程池的SwitchActiveThread成员函数,该函数用以将线程从活动状态转变为空闲态。也就是从活动线程链表转移到空闲线程栈中。同时线程继续等待事件对象触发。

在此函数内部,在转换之前会检查任务队列中是否还有任务,如果有任务,线程会继续从任务队列取出任务继续执行,而不会切换到空闲态。直到任务队列中没有任务时才会执行状态切换操作。

CMyThreadPool线程池类

任务队列、活动线程链表、空闲线程队列都作为线程池的成员变量,由线程池维护。

类声明如下:

  1. //MyThreadPool.h
  2. #pragma once
  3. #include<list>
  4. #include "MyMutex.h"
  5. #include "MyStack.h"
  6. #include "MyList.h"
  7. #include"MyQueue.h"
  8. class CMyThread;
  9. class CTask;
  10. enum PRIORITY
  11. {
  12. NORMAL,
  13. HIGH
  14. };
  15. class CBaseThreadPool
  16. {
  17. public:
  18. virtual CMyThread* PopIdleThread()=0;
  19. virtual CTask*GetNewTask()=0;
  20. //virtual bool  ExecuteNewTask(CTask *task)=0;
  21. virtual bool SwitchActiveThread(CMyThread*)=0;
  22. };
  23. class CMyThreadPool:public CBaseThreadPool
  24. {
  25. public:
  26. CMyThreadPool(int num);
  27. ~CMyThreadPool(void);
  28. public:
  29. virtual CMyThread* PopIdleThread();
  30. virtual bool SwitchActiveThread(CMyThread*);
  31. virtual CTask*GetNewTask();
  32. public:
  33. //priority为优先级。高优先级的任务将被插入到队首。
  34. bool addTask(CTask*t,PRIORITY priority);
  35. bool start();//开始调度。
  36. bool destroyThreadPool();
  37. private:
  38. int m_nThreadNum;
  39. bool m_bIsExit;
  40. CMyStack m_IdleThreadStack;
  41. CMyList m_ActiveThreadList;
  42. CMyQueue m_TaskQueue;
  43. };
//MyThreadPool.h
#pragma once
#include<list>
#include "MyMutex.h"
#include "MyStack.h"
#include "MyList.h"
#include"MyQueue.h"
class CMyThread;
class CTask;
enum PRIORITY
{
NORMAL,
HIGH
};
class CBaseThreadPool
{
public:
virtual CMyThread* PopIdleThread()=0;
virtual CTask*GetNewTask()=0;
//virtual bool ExecuteNewTask(CTask *task)=0;
virtual bool SwitchActiveThread(CMyThread*)=0;
};
class CMyThreadPool:public CBaseThreadPool
{
public:
CMyThreadPool(int num);
~CMyThreadPool(void);
public:
virtual CMyThread* PopIdleThread();
virtual bool SwitchActiveThread(CMyThread*);
virtual CTask*GetNewTask();
public:
//priority为优先级。高优先级的任务将被插入到队首。
bool addTask(CTask*t,PRIORITY priority);
bool start();//开始调度。
bool destroyThreadPool();
private:
int m_nThreadNum;
bool m_bIsExit; CMyStack m_IdleThreadStack;
CMyList m_ActiveThreadList;
CMyQueue m_TaskQueue;
};

addTask函数用于向任务队列中添加任务。添加任务后,会检查空闲线程堆栈中是否为空,如不为空则弹出栈顶线程执行任务。

  1. bool CMyThreadPool::addTask( CTask*t,PRIORITY priority )
  2. {
  3. assert(t);
  4. if(!t||m_bIsExit)
  5. return false;
  6. CTask *task=NULL;
  7. if(priority==PRIORITY::NORMAL)
  8. {
  9. m_TaskQueue.push(t);//压入任务队列尾部。
  10. }
  11. else if(PRIORITY::HIGH)
  12. {
  13. m_TaskQueue.pushFront(t);//高优先级任务,压到队首。
  14. }
  15. if(!m_IdleThreadStack.isEmpty())//存在空闲线程。调用空闲线程处理任务。
  16. {
  17. task=m_TaskQueue.pop();//取出列头任务。
  18. if(task==NULL)
  19. {
  20. //std::cout<<"任务取出出错。"<<std::endl;
  21. return 0;
  22. }
  23. CMyThread*pThread=PopIdleThread();
  24. m_ActiveThreadList.addThread(pThread);//加入到活动链表。
  25. pThread->assignTask(task);//将任务与线程关联。
  26. pThread->startTask();//开始任务,内部对事件对象进行触发。
  27. }
  28. }
bool CMyThreadPool::addTask( CTask*t,PRIORITY priority )
{
assert(t);
if(!t||m_bIsExit)
return false;
CTask *task=NULL;
if(priority==PRIORITY::NORMAL)
{
m_TaskQueue.push(t);//压入任务队列尾部。
}
else if(PRIORITY::HIGH)
{
m_TaskQueue.pushFront(t);//高优先级任务,压到队首。
}
if(!m_IdleThreadStack.isEmpty())//存在空闲线程。调用空闲线程处理任务。
{
task=m_TaskQueue.pop();//取出列头任务。
if(task==NULL)
{
//std::cout<<"任务取出出错。"<<std::endl;
return 0;
}
CMyThread*pThread=PopIdleThread();
m_ActiveThreadList.addThread(pThread);//加入到活动链表。
pThread->assignTask(task);//将任务与线程关联。
pThread->startTask();//开始任务,内部对事件对象进行触发。
} }

switchActiveThread函数用以在线程结束任务之后,将自己切换到空闲态。在切换之前会检查任务队列是否有任务,如有任务,则取出继续执行。直到任务队列为空时,才将自己切换到空闲态。由各线程类对象调用。

  1. bool CMyThreadPool::SwitchActiveThread( CMyThread*t)
  2. {
  3. if(!m_TaskQueue.isEmpty())//任务队列不为空,继续取任务执行。
  4. {
  5. CTask *pTask=NULL;
  6. pTask=m_TaskQueue.pop();
  7. t->assignTask(pTask);
  8. t->startTask();
  9. }
  10. else//任务队列为空,该线程挂起。
  11. {
  12. m_ActiveThreadList.removeThread(t);
  13. m_IdleThreadStack.push(t);
  14. }
  15. return true;
  16. }
bool CMyThreadPool::SwitchActiveThread( CMyThread*t)
{
if(!m_TaskQueue.isEmpty())//任务队列不为空,继续取任务执行。
{
CTask *pTask=NULL;
pTask=m_TaskQueue.pop();
t->assignTask(pTask);
t->startTask();
}
else//任务队列为空,该线程挂起。
{
m_ActiveThreadList.removeThread(t);
m_IdleThreadStack.push(t);
}
return true;
}

代码地址:https://github.com/ithzhang/ThreadpoolLib.git

C++实现线程池 .的更多相关文章

  1. 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)

    前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...

  2. C#多线程之线程池篇3

    在上一篇C#多线程之线程池篇2中,我们主要学习了线程池和并行度以及如何实现取消选项的相关知识.在这一篇中,我们主要学习如何使用等待句柄和超时.使用计时器和使用BackgroundWorker组件的相关 ...

  3. C#多线程之线程池篇2

    在上一篇C#多线程之线程池篇1中,我们主要学习了如何在线程池中调用委托以及如何在线程池中执行异步操作,在这篇中,我们将学习线程池和并行度.实现取消选项的相关知识. 三.线程池和并行度 在这一小节中,我 ...

  4. C#多线程之线程池篇1

    在C#多线程之线程池篇中,我们将学习多线程访问共享资源的一些通用的技术,我们将学习到以下知识点: 在线程池中调用委托 在线程池中执行异步操作 线程池和并行度 实现取消选项 使用等待句柄和超时 使用计时 ...

  5. NGINX引入线程池 性能提升9倍

    1. 引言 正如我们所知,NGINX采用了异步.事件驱动的方法来处理连接.这种处理方式无需(像使用传统架构的服务器一样)为每个请求创建额外的专用进程或者线程,而是在一个工作进程中处理多个连接和请求.为 ...

  6. Java线程池解析

    Java的一大优势是能完成多线程任务,对线程的封装和调度非常好,那么它又是如何实现的呢? jdk的包下和线程相关类的类图. 从上面可以看出Java的线程池主的实现类主要有两个类ThreadPoolEx ...

  7. Android线程管理之ExecutorService线程池

    前言: 上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Execu ...

  8. Android线程管理之ThreadPoolExecutor自定义线程池

    前言: 上篇主要介绍了使用线程池的好处以及ExecutorService接口,然后学习了通过Executors工厂类生成满足不同需求的简单线程池,但是有时候我们需要相对复杂的线程池的时候就需要我们自己 ...

  9. -Android -线程池 批量上传图片 -附php接收代码

    (出处:http://www.cnblogs.com/linguanh/) 目录: 1,前序 2,类特点 3,用法 4,java代码 5,php代码 1,前序 还是源于重构,看着之前为赶时间写着的碎片 ...

  10. C#多线程--线程池(ThreadPool)

    先引入一下线程池的概念: 百度百科:线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使用默认的堆栈大小,以默认的优先级运行, ...

随机推荐

  1. FMDB将对象放进数据库[二](使用runtime)

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  2. TensorFlow深度学习笔记 Tensorboard入门

    转载请注明作者:梦里风林 Github工程地址:https://github.com/ahangchen/GDLnotes 欢迎star,有问题可以到Issue区讨论 官方教程: https://ww ...

  3. 定制化Azure站点Java运行环境(2)

    Azure Website上发布Java web应用 在Azure站点上发布Java Web应用非常简单,可以使用git从源代码发布,也可以使用FTP/FTPs直接发布,本节介绍FTP方式. 准备好你 ...

  4. js的体会

    关于观察者模式的核心是: 回调函数, 传递函数名作为参数,或者是传递变量,然后调用其函数名. 关于闭包的核心是 闭包的函数是全局变量之下的函数, 而非闭包的函数是局部变量. <script> ...

  5. PHP内核学习(一)SAPI

    学习PHP-src之前,我准备了一份源文件: GitHub下载->https://github.com/helingfeng/php-src 简单分析一下源码的目录结构: 1. root根目录下 ...

  6. block的一点知识

    一个block的笔记: http://www.cnblogs.com/xinye/archive/2013/03/03/2941203.html http://segmentfault.com/q/1 ...

  7. Android学习之SharedPreferences类

    SharedPreferences类 android.content.SharedPreferences 类概括: 访问和修改由函数getSharedPreferences(String,int)返回 ...

  8. Error: Linux下 mysql.sock文件丢失被删除解决方法

    在默认情况下,Mysql安装以后会在/tmp目录下生成一个mysql.sock文件,如该文件丢失则Mysql将不能够正常启动,解决方法:使用mysqld_safe 启动即可解决: #basedir:m ...

  9. iOS 各种传值方式

    属性传值 将A页面所拥有的信息通过属性传递到B页面使用 B页面定义了一个naviTitle属性,在A页面中直接通过属性赋值将A页面中的值传到B页面. A页面DetailViewController.h ...

  10. Nginx学习——http配置项解析编程

    http配置项解析编程 配置config ngx_addon_name=ngx_http_mytest_module HTTP_MODULES="$HTTP_MODULES ngx_http ...