基于Linux/C++简单线程池的实现

我们知道Java语言对于多线程的支持十分丰富,JDK本身提供了很多性能优良的库,包括ThreadPoolExecutor和ScheduleThreadPoolExecutor等。C++11中的STL也提供了std:thread(然而我还没有看,这里先占个坑)还有很多第三方库的实现。这里我重复“造轮子”的目的还是为了深入理解C++和Linux线程基础概念,主要以学习的目的。

首先,为什么要使用线程池。因为线程的创建、和清理都是需要耗费系统资源的。我们知道Linux中线程实际上是由轻量级进程实现的,相对于纯理论上的线程这个开销还是有的。假设某个线程的创建、运行和销毁的时间分别为T1、T2、T3,当T1+T3的时间相对于T2不可忽略时,线程池的就有必要引入了,尤其是处理数百万级的高并发处理时。线程池提升了多线程程序的性能,因为线程池里面的线程都是现成的而且能够重复使用,我们不需要临时创建大量线程,然后在任务结束时又销毁大量线程。一个理想的线程池能够合理地动态调节池内线程数量,既不会因为线程过少而导致大量任务堆积,也不会因为线程过多了而增加额外的系统开销。

其实线程池的原理非常简单,它就是一个非常典型的生产者消费者同步问题。根据刚才描述的线程池的功能,可以看出线程池至少有两个主要动作,一个是主程序不定时地向线程池添加任务,另一个是线程池里的线程领取任务去执行。且不论任务和执行任务是个什么概念,但是一个任务肯定只能分配给一个线程执行。这样就可以简单猜想线程池的一种可能的架构了:主程序执行入队操作,把任务添加到一个队列里面;池子里的多个工作线程共同对这个队列试图执行出队操作,这里要保证同一时刻只有一个线程出队成功,抢夺到这个任务,其他线程继续共同试图出队抢夺下一个任务。所以在实现线程池之前,我们需要一个队列。这里的生产者就是主程序,生产任务(增加任务),消费者就是工作线程,消费任务(执行、减少任务)。因为这里涉及到多个线程同时访问一个队列的问题,所以我们需要互斥锁来保护队列,同时还需要条件变量来处理主线程通知任务到达、工作线程抢夺任务的问题。

一般来说实现一个线程池主要包括以下4个组成部分:

  1. 线程管理器:用于创建并管理线程池。
  2. 工作线程:线程池中实际执行任务的线程。在初始化线程时会预先创建好固定数目的线程在池中,这些初始化的线程一般处于空闲状态。
  3. 任务接口:每个任务必须实现的接口。当线程池的任务队列中有可执行任务时,被空间的工作线程调去执行(线程的闲与忙的状态是通过互斥量实现的),把任务抽象出来形成一个接口,可以做到线程池与具体的任务无关。
  4. 任务队列:用来存放没有处理的任务。提供一种缓冲机制。实现这种结构有很多方法,常用的有队列和链表结构。

流程图如下:

895794-20170708215223081-1855294186.jpg

ool.h

 
  1. #ifndef __THREAD_POOL_H

  2. #define __THREAD_POOL_H

  3.  
  4. #include <vector>

  5. #include <string>

  6. #include <pthread.h>

  7.  
  8. using namespace std;

  9.  
  10. /*执行任务的类:设置任务数据并执行*/

  11. class CTask {

  12. protected:

  13. string m_strTaskName; //任务的名称

  14. void* m_ptrData; //要执行的任务的具体数据

  15.  
  16. public:

  17. CTask() = default;

  18. CTask(string &taskName): m_strTaskName(taskName), m_ptrData(NULL) {}

  19. virtual int Run() = 0;

  20. void setData(void* data); //设置任务数据

  21.  
  22. virtual ~CTask() {}

  23.  
  24. };

  25.  
  26. /*线程池管理类*/

  27. class CThreadPool {

  28. private:

  29. static vector<CTask*> m_vecTaskList; //任务列表

  30. static bool shutdown; //线程退出标志

  31. int m_iThreadNum; //线程池中启动的线程数

  32. pthread_t *pthread_id;

  33.  
  34. static pthread_mutex_t m_pthreadMutex; //线程同步锁

  35. static pthread_cond_t m_pthreadCond; //线程同步条件变量

  36.  
  37. protected:

  38. static void* ThreadFunc(void *threadData); //新线程的线程回调函数

  39. static int MoveToIdle(pthread_t tid); //线程执行结束后,把自己放入空闲线程中

  40. static int MoveToBusy(pthread_t tid); //移入到忙碌线程中去

  41. int Create(); //创建线程池中的线程

  42.  
  43. public:

  44. CThreadPool(int threadNum);

  45. int AddTask(CTask *task); //把任务添加到任务队列中

  46. int StopAll(); //使线程池中的所有线程退出

  47. int getTaskSize(); //获取当前任务队列中的任务数

  48. };

  49.  
  50. #endif

2 thread_pool.cpp

 
  1. #include "thread_pool.h"

  2. #include <cstdio>

  3.  
  4. void CTask::setData(void* data) {

  5. m_ptrData = data;

  6. }

  7.  
  8. //静态成员初始化

  9. vector<CTask*> CThreadPool::m_vecTaskList;

  10. bool CThreadPool::shutdown = false;

  11. pthread_mutex_t CThreadPool::m_pthreadMutex = PTHREAD_MUTEX_INITIALIZER;

  12. pthread_cond_t CThreadPool::m_pthreadCond = PTHREAD_COND_INITIALIZER;

  13.  
  14. //线程管理类构造函数

  15. CThreadPool::CThreadPool(int threadNum) {

  16. this->m_iThreadNum = threadNum;

  17. printf("I will create %d threads.\n", threadNum);

  18. Create();

  19. }

  20.  
  21. //线程回调函数

  22. void* CThreadPool::ThreadFunc(void* threadData) {

  23. pthread_t tid = pthread_self();

  24. while(1)

  25. {

  26. pthread_mutex_lock(&m_pthreadMutex);

  27. //如果队列为空,等待新任务进入任务队列

  28. while(m_vecTaskList.size() == 0 && !shutdown)

  29. pthread_cond_wait(&m_pthreadCond, &m_pthreadMutex);

  30.  
  31. //关闭线程

  32. if(shutdown)

  33. {

  34. pthread_mutex_unlock(&m_pthreadMutex);

  35. printf("[tid: %lu]\texit\n", pthread_self());

  36. pthread_exit(NULL);

  37. }

  38.  
  39. printf("[tid: %lu]\trun: ", tid);

  40. vector<CTask*>::iterator iter = m_vecTaskList.begin();

  41. //取出一个任务并处理之

  42. CTask* task = *iter;

  43. if(iter != m_vecTaskList.end())

  44. {

  45. task = *iter;

  46. m_vecTaskList.erase(iter);

  47. }

  48.  
  49. pthread_mutex_unlock(&m_pthreadMutex);

  50.  
  51. task->Run(); //执行任务

  52. printf("[tid: %lu]\tidle\n", tid);

  53.  
  54. }

  55.  
  56. return (void*)0;

  57. }

  58.  
  59. //往任务队列里添加任务并发出线程同步信号

  60. int CThreadPool::AddTask(CTask *task) {

  61. pthread_mutex_lock(&m_pthreadMutex);

  62. m_vecTaskList.push_back(task);

  63. pthread_mutex_unlock(&m_pthreadMutex);

  64. pthread_cond_signal(&m_pthreadCond);

  65.  
  66. return 0;

  67. }

  68.  
  69. //创建线程

  70. int CThreadPool::Create() {

  71. pthread_id = new pthread_t[m_iThreadNum];

  72. for(int i = 0; i < m_iThreadNum; i++)

  73. pthread_create(&pthread_id[i], NULL, ThreadFunc, NULL);

  74.  
  75. return 0;

  76. }

  77.  
  78. //停止所有线程

  79. int CThreadPool::StopAll() {

  80. //避免重复调用

  81. if(shutdown)

  82. return -1;

  83. printf("Now I will end all threads!\n\n");

  84.  
  85. //唤醒所有等待进程,线程池也要销毁了

  86. shutdown = true;

  87. pthread_cond_broadcast(&m_pthreadCond);

  88.  
  89. //清楚僵尸

  90. for(int i = 0; i < m_iThreadNum; i++)

  91. pthread_join(pthread_id[i], NULL);

  92.  
  93. delete[] pthread_id;

  94. pthread_id = NULL;

  95.  
  96. //销毁互斥量和条件变量

  97. pthread_mutex_destroy(&m_pthreadMutex);

  98. pthread_cond_destroy(&m_pthreadCond);

  99.  
  100. return 0;

  101. }

  102.  
  103. //获取当前队列中的任务数

  104. int CThreadPool::getTaskSize() {

  105. return m_vecTaskList.size();

  106. }

3 main.cpp

 
  1. #include "thread_pool.h"

  2. #include <cstdio>

  3. #include <stdlib.h>

  4. #include <unistd.h>

  5.  
  6. class CMyTask: public CTask {

  7. public:

  8. CMyTask() = default;

  9. int Run() {

  10. printf("%s\n", (char*)m_ptrData);

  11. int x = rand()%4 + 1;

  12. sleep(x);

  13. return 0;

  14. }

  15. ~CMyTask() {}

  16. };

  17.  
  18. int main() {

  19. CMyTask taskObj;

  20. char szTmp[] = "hello!";

  21. taskObj.setData((void*)szTmp);

  22. CThreadPool threadpool(5); //线程池大小为5

  23.  
  24. for(int i = 0; i < 10; i++)

  25. threadpool.AddTask(&taskObj);

  26.  
  27. while(1) {

  28. printf("There are still %d tasks need to handle\n", threadpool.getTaskSize());

  29. //任务队列已没有任务了

  30. if(threadpool.getTaskSize()==0) {

  31. //清除线程池

  32. if(threadpool.StopAll() == -1) {

  33. printf("Thread pool clear, exit.\n");

  34. exit(0);

  35. }

  36. }

  37. sleep(2);

  38. printf("2 seconds later...\n");

  39. }

  40. return 0;

  41. }

4 Makefile

 
  1. CC:= g++

  2. TARGET:= threadpool

  3. INCLUDE:= -I./

  4. LIBS:= -lpthread

  5. # C++语言编译参数

  6. CXXFLAGS:= -std=c++11 -g -Wall -D_REENTRANT

  7. # C预处理参数

  8. # CPPFLAGS:=

  9. OBJECTS :=thread_pool.o main.o

  10.  
  11. $(TARGET): $(OBJECTS)

  12. $(CC) -o $(TARGET) $(OBJECTS) $(LIBS)

  13.  
  14. # $@表示所有目标集

  15. %.o:%.cpp

  16. $(CC) -c $(CXXFLAGS) $(INCLUDE) $< -o $@

  17.  
  18. .PHONY : clean

  19. clean:

  20. -rm -f $(OBJECTS) $(TARGET)

5 输出结果

 
  1. I will create 5 threads.

  2. There are still 10 tasks need to handle

  3. [tid: 140056759576320] run: hello!

  4. [tid: 140056751183616] run: hello!

  5. [tid: 140056742790912] run: hello!

  6. [tid: 140056734398208] run: hello!

  7. [tid: 140056767969024] run: hello!

  8. 2 seconds later...

  9. There are still 5 tasks need to handle

  10. [tid: 140056742790912] idle

  11. [tid: 140056742790912] run: hello!

  12. [tid: 140056767969024] idle

  13. [tid: 140056767969024] run: hello!

  14. [tid: 140056751183616] idle

  15. [tid: 140056751183616] run: hello!

  16. [tid: 140056759576320] idle

  17. [tid: 140056759576320] run: hello!

  18. [tid: 140056751183616] idle

  19. [tid: 140056751183616] run: hello!

  20. [tid: 140056734398208] idle

  21. 2 seconds later...

  22. There are still 0 tasks need to handle

  23. Now I will end all threads!

  24. 2 seconds later...

  25. [tid: 140056734398208] exit

  26. [tid: 140056767969024] idle

  27. [tid: 140056767969024] exit

  28. [tid: 140056759576320] idle

  29. [tid: 140056759576320] exit

  30. [tid: 140056751183616] idle

  31. [tid: 140056751183616] exit

  32. [tid: 140056742790912] idle

  33. [tid: 140056742790912] exit

  34. 2 seconds later...

  35. There are still 0 tasks need to handle

  36. Thread pool clear, exit.

扩展资料:

线程池设计中的惊群问题

C 实现有追求的线程池 探究

高效线程池(threadpool)的实现

转载于:https://www.cnblogs.com/alwayswangzi/p/7138154.html

相关资源:C++线程实例

(3条消息) 基于Linux/C++简单线程池的实现_weixin_30457881的博客-CSDN博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值