簡易線程池的實現

1.線程池基本瞭解

一種線程使用模式。線程過多會帶來調度開銷,進而影響緩存局部性和整體性能。而線程池維護着多個線程,等待着監督管理者分配可併發執行的任務。這避免了在處理短時間任務時創建與銷燬線程的代價。線程池不僅能夠保證內核的充分利用,還能防止過分調度。可用線程數量應該取決於可用的併發處理器、處理器內核、內存、網絡sockets等的數量。

2.線程池的應用場景

1)需要大量的線程來完成任務,且完成任務的時間比較短。 WEB服務器完成網頁請求這樣的任務,使用線程池技術是非常合適的。因爲單個任務小,而任務數量巨大,你可以想象一個熱門網站的點擊次數。 但對於長時間的任務,比如一個Telnet連接請求,線程池的優點就不明顯了。因爲Telnet會話時間比線程的創建時間大多了。
2)對性能要求苛刻的應用,比如要求服務器迅速響應客戶請求。
3) 接受突發性的大量請求,但不至於使服務器因此產生大量線程的應用。突發性大量客戶請求,在沒有線程池情況下,將產生大量線程,雖然理論上大部分操作系統線程數目最大值不是問題,短時間內產生大量線程可能使內存到達極限,出現錯誤.

3.實現一個簡單的加法任務的線程池

1)首先我們創建一個任務類。

//回調函數
typedef int(*cal_t)(int,int);

class Task{
  private:
    int x;//操作數1
    int y;//操作數2
    int z;//運算結果
    cal_t handler_task;//運算操作
  public:
    Task(int a,int b,cal_t handler_task_)//構造函數
    :x(a),y(b),handler_task(handler_task_)
    {

    }
   //執行操作
     void Run()
    {
      z = handler_task(x,y);
    }
   //將結果輸出
    void Show()
    {
      cout<<"thread : "<<pthread_self()<<"Task finish,result is : "<<z<<endl;
    }

    ~Task()
    {

    }
};


2)線程池類

《1》線程池的成員

class  ThreadPool{
   private:
     queue<Task> Task_Queue;//將任務添加在一個隊列中
     bool IsStop;//線程池的工作狀態
     int ThreadNum;//線程池內的線程數量
     //爲了線程安全所以需要互斥鎖與條件變量保護臨界資源
     pthread_mutex_t lock;
     pthread_cond_t cond;
};

《2》根據測試實現線程池內的成員函數

#include"ThreadPool.hpp"

#define NUM 5 //設置線程池的線程數量
//任務操作
int Add(int x,int y)
{
  return x + y;
}

int main()
{
//創建一個線程池類
  ThreadPool *tp =new ThreadPool(NUM);
  //線程池的初始化
  tp -> InitThreadPool();
//爲了便於理解,我們使用while循環先一直往任務隊列添加任務。
  int count =1;
  while(1){
    sleep(1);
    Task t(count,count-1,Add);
    tp->AddTask(t);
    count++;
  }
  return 0;
}

a.線程池類的構造函數與析構函數

//構造函數初始化時,將IsStop狀態設置爲fasle,否則線程池無法工作。
 ThreadPool(int num):ThreadNum(num),IsStop(false)
     {}

b.線程池的初始化

 void InitThreadPool()
     {
     //對互斥鎖以及條件變臉初始化。
       pthread_mutex_init(&lock,NULL);
       pthread_cond_init(&cond,NULL);
       //設置變量控制線程的創建
       int i = 0;
       for(;i<ThreadNum;i++)
       {
          pthread_t tid;
          pthread_create(&tid,NULL,route,(void *)this);
       }
     }

//析構函數完成互斥鎖以及條件變量的銷燬
~ThreadPool()
     {
          pthread_mutex_destroy(&lock);
          pthread_cond_destroy(&cond);
     }

c.初始化完後實現線程創建中的route函數

//C++中static函數內無this指針,C++類對線程操作要用static修飾
 static  void *route(void *arg)
     {
        ThreadPool *tp = (ThreadPool*)arg;
        //將線程自己分離出來
        pthread_detach(pthread_self());
        while(1){
        //爲了保證線程安全,將任務隊列加鎖
          tp->LockQueue();
          //當任務隊列爲空時,線程先進入等待狀態
          if(tp->IsEmpty()){
              tp->IdleThread();
          }
          //當隊列有任務時,創建一個任務類並得到該任務,也由此得出需要一個AddTask();成員函數。
          Task t = tp->GetTask();
          //取到會解鎖
          tp->UnlockQueue();
          //執行任務操作
          t.Run();
          //將任務結果顯示
          t.Show();
        }
     }

d.依次實LockQueue();IsEmpty();IDleThread();GetTask();UnlockQueue();

    //任務隊列加鎖
     void LockQueue()
     {
       pthread_mutex_lock(&lock);
     }
     //任務解鎖解鎖
     void UnlockQueue()
     {
       pthread_mutex_unlock(&lock);
     }
     //判斷任務是否爲空
     bool IsEmpty()
     {
       return Task_Queue.size() == 0 ? true : false;
     }
     //加任務
     void AddTask(Task &t)
     {
      Task_Queue.push(t);
      //當得到一個任務時便傳送信號給一個進程
      NoticOneThread();
      UnlockQueue();
     }
     //傳送信號給一個進程
      void NoticOneThread()
     {
       pthread_cond_signal(&cond);
     }
        //等待隊列
     void IdleThread()
     {
       pthread_cond_wait(&cond,&lock);
     }
    //得到任務
     Task GetTask()
     {
       Task t = Task_Queue.front();
       Task_Queue.pop();
       return t;
     }

e.暫停分析
貌似我們已經實現了一個加法的簡易線程池,首先創建一個線程池對象,然後進行初始化,創建設定數的線程,並且它們都處於等待狀態,當有任務時,它們依次被喚醒,執行任務。但是我們測試用的是while(1)假如沒有了任務,線程便一直處於了等待狀態不會退出,於是我們還要實現一個Stop()成員函數,若停止了廣播NoticAllThread()中的線程讓他們依次退出,並修改IdleThread()以及AddTask()函數進行IsStop條件判斷。

 void Stop()
     {
       LockQueue();
       IsStop = true;
       UnlockQueue();
       while(ThreadNum > 0){
         NoticAllThread();
       }
     }
 void IdleThread()
     {
       if(IsStop){
         UnlockQueue();
         ThreadNum--;
         pthread_exit((void *)0);
         cout<<"pthread "<<pthread_self() <<"quit"<<endl;
         return ;
       }
       pthread_cond_wait(&cond,&lock);
     }
       void AddTask(Task &t)
     {
       if(IsStop)
       {
         UnlockQueue();
         return ;
       }
      Task_Queue.push(t);
      NoticOneThread();
      UnlockQueue();
     }
    
     void NoticAllThread()
     {
       pthread_cond_broadcast(&cond);
     }


3) 完整代碼以及測試

頭文件

#ifndef __THREADPOLL_HPP__
#define __THREADPOLL_HPP__ 

#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<queue>

using namespace std;


typedef int(*cal_t)(int,int);

class Task{
  private:
    int x;
    int y;
    int z;
    cal_t handler_task;
  public:
    Task(int a,int b,cal_t handler_task_)
    :x(a),y(b),handler_task(handler_task_)
    {

    }

    void Run()
    {
      z = handler_task(x,y);
    }

    void Show()
    {
      cout<<"thread : "<<pthread_self()<<"Task finish,result is : "<<z<<endl;
    }

    ~Task()
    {

    }
};

class  ThreadPool{
   private:
     queue<Task> Task_Queue;
     bool IsStop;
     int ThreadNum;
     pthread_mutex_t lock;
     pthread_cond_t cond;



  private:
    static  void *route(void *arg)
     {
        ThreadPool *tp = (ThreadPool*)arg;
        pthread_detach(pthread_self());
        while(1){
          tp->LockQueue();
          if(tp->IsEmpty()){
              tp->IdleThread();
          }
          Task t = tp->GetTask();
          tp->UnlockQueue();
          
          t.Run();
          t.Show();
        }
     }

     void NoticOneThread()
     {
       pthread_cond_signal(&cond);
     }

     void NoticAllThread()
     {
       pthread_cond_broadcast(&cond);
     }
   public:
     ThreadPool(int num):ThreadNum(num),IsStop(false)
     {}

     void InitThreadPool()
     {
       pthread_mutex_init(&lock,NULL);
       pthread_cond_init(&cond,NULL);
       int i = 0;
      
       
       for(;i<ThreadNum;i++)
       {
          pthread_t tid;
          pthread_create(&tid,NULL,route,(void *)this);
       }
     }

     ~ThreadPool()
     {
          pthread_mutex_destroy(&lock);
          pthread_cond_destroy(&cond);
     }
    

     void LockQueue()
     {
       pthread_mutex_lock(&lock);
     }

     void UnlockQueue()
     {
       pthread_mutex_unlock(&lock);
     }
     
     bool IsEmpty()
     {
       return Task_Queue.size() == 0 ? true : false;
     }

     void AddTask(Task &t)
     {
       if(IsStop)
       {
         UnlockQueue();
         return ;
       }
      Task_Queue.push(t);
      NoticOneThread();
      UnlockQueue();
     }
        
     void IdleThread()
     {
       if(IsStop){
         UnlockQueue();
         ThreadNum--;
         pthread_exit((void *)0);
         cout<<"pthread "<<pthread_self() <<"quit"<<endl;
         return ;
       }
       pthread_cond_wait(&cond,&lock);
     }

     Task GetTask()
     {
       Task t = Task_Queue.front();
       Task_Queue.pop();
       return t;
     }

     void Stop()
     {
       LockQueue();
       IsStop = true;
       UnlockQueue();
       while(ThreadNum > 0){
         NoticAllThread();
       }
     }

};



#endif 

測試文件
我們只設置九個任務。

#include"ThreadPool.hpp"

#define NUM 5

int Add(int x,int y)
{
  return x + y;
}

int main()
{
  ThreadPool *tp =new ThreadPool(NUM);
  tp -> InitThreadPool();

  int count =1;
  int u = 0;
  for(;u<1;u++){
    sleep(1);
    Task t(count,count-1,Add);
    tp->AddTask(t);
    count++;
  }
  return 0;
}

結果
在gdb下觀察:
1)首先創建5個線程。
在這裏插入圖片描述
2)5個線程桉序執行任務。
在這裏插入圖片描述
3)沒有任務了線程退出。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章