【C++】勉強能看的線程池詳解

在這裏插入圖片描述

線程池這東西,用了幾次還是不得其解,簡直是:求之不得,寤寐思服。悠哉悠哉,輾轉反側。

什麼是線程池?爲什麼要用線程池?

線程池,好東西啊,它有一池子的線程,所以叫線程池。
爲什麼說它是好東西呢?有的人會覺得,那一池子線程,放在那邊又不用,不浪費資源?
其實這筆賬很好算的:假設一個服務器完成一項任務所需時間爲:T1 創建線程時間,T2 在線程中執行任務的時間,T3 銷燬線程時間。
T1+T2 > T3 && 這種線程被多次調度的時候,你還會覺得浪費資源嗎?況且線程池內部又不是缺乏管理,相反,線程池內部管理很嚴格,喫白飯的線程很難有立足之地,用不上就裁員唄。

多線程技術主要解決處理器單元內多個線程執行的問題,它可以顯著減少處理器單元的閒置時間,增加處理器單元的吞吐能力。

線程池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高服務器程序性能的。它把T1,T3分別安排在服務器程序的啓動和結束的時間段或者一些空閒的時間段,這樣在服務器程序處理客戶請求時,不會有T1,T3的開銷了。

線程池不僅調整T1,T3產生的時間段,而且它還顯著減少了創建線程的數目
看一個例子:
假設一個服務器一天要處理50000個請求,並且每個請求需要一個單獨的線程完成。在線程池中,線程數一般是固定的,所以產生線程總數不會超過線程池中線程的數目,而如果服務器不利用線程池來處理這些請求則線程總數爲50000。一般線程池大小是遠小於50000。所以利用線程池的服務器程序不會爲了創建50000而在處理請求時浪費時間,從而提高效率。


線程池的組成部分如下:

1、線程池管理器(ThreadPool):用於創建並管理線程池,包括 創建線程池,銷燬線程池,添加新任務;
2、工作線程(PoolWorker):線程池中線程,在沒有任務時處於等待狀態,可以循環的執行任務;
3、任務接口(Task):每個任務必須實現的接口,以供工作線程調度任務的執行,它主要規定了任務的入口,任務執行完後的收尾工作,任務的執行狀態等;
4、任務隊列(taskQueue):用於存放沒有處理的任務。提供一種緩衝機制。

線程池的外部支持還有:

5、鎖
6、條件變量

這,就是線程池。


示例代碼與分析

還是配上代碼來講,不然我自己也暈。

//E_Pthread_Pool.h 我的代碼的頭文件

#ifndef E_EPOLL_POOL_H
#define E_EPOLL_POOL_H

#include <pthread.h>
#include <unistd.h>
#include <list>	//據說list不安全,不安全就不安全吧,更不安全的都忍了
#include "d_pthread_cond.h"	//封裝過的條件變量類,繼承自封裝的mutex鎖類,所以具有鎖和條件變量的雙重屬性

using namespace std;

class Task	//任務接口,每個任務必須實現的接口,以供工作線程調度任務的執行
{
public:
    Task(){}
    virtual ~Task(){}
    virtual int run()=0; //留給子類實現
};

typedef list<Task *> list_task; //任務隊列,用於暫存等待處理的任務,等待線程喚醒時處理,提供一種緩衝機制。

class E_PThread_Pool	//線程池類
{
public:
    E_PThread_Pool(unsigned int max=100,unsigned int min=10,unsigned int wait=60);
    ~E_PThread_Pool();
    void addTask(Task *task);	// 往任務隊列中添加新線程

private:
    static void *taskThread(void *arg);// 工作線程
    void createThread();		// 新建一個線程
    void destroyThread();		// 銷燬一個線程池
    
    unsigned int maxcount;		// 最大線程數
    unsigned int mincount; 		// 最小線程數
    unsigned int count;	 		// 當前線程池中線程數
    unsigned int waitcount; 	// 等待線程數
    unsigned int waitsec;		// 等待時間
    list_task	 taskList;      //任務隊列
    D_Pthread_Cond taskCond;    //任務鎖,線程接任務時使用
    D_Pthread_Cond cond;        //線程鎖,創建線程時使用
    bool Stop;                  //線程池是否被允許運作,初始化線程池對象時置0,線程池銷燬時置爲1
};

#endif
//我的線程池源文件

#include "e_pthread_pool.h"

//開放接口1
E_PThread_Pool::E_PThread_Pool(unsigned int max,unsigned int min,unsigned int wait)
{
    //配置基本參數
    count = 0;		//當前線程池爲空
    waitcount = 0;  //沒有等待線程
    mincount = min;	//核心線程數(出廠配置)
    maxcount = max;	//最大線程數(能承受的最高配置)
    waitsec = wait;	//線程保活時長(過了時長還沒接到任務,那就裁掉)
    Stop = false;	//允許運作

    //上鎖,創建一定數量的線程作爲初始線程池
    cond.lock();
        for (unsigned i = 0; i < mincount; i++)
        {
            createThread();	//跳轉到這個函數的實現->->->->->
        }
    cond.unlock();
}

E_PThread_Pool::~E_PThread_Pool()
{
    destroyThread();	//銷燬線程池
}

void E_PThread_Pool::createThread()
{
    pthread_t tid;
    int ret = pthread_create(&tid, NULL, taskThread, (void *)this);	
    //以執行taskThread()爲目的創建線程,跳轉到taskThread()函數的實現 ->->->->->
   
    if (ret < 0)
        perror("pthread create error");
    else
        count++;
}

// 工作線程
void * E_PThread_Pool::taskThread(void *arg)
{
    pthread_detach(pthread_self()); //設置線程自分離屬性
    E_PThread_Pool *pool=(E_PThread_Pool *)arg;
    while(1)
    {
        pool->cond.lock();

//如果沒有工作線程在等待
        if (pool->taskList.empty())
        {
            if(pool->Stop)	//當收到線程池停止運行的消息時
            {
                pool->count--;	//線程數減一
                pool->cond.unlock();	
                pthread_exit(NULL); //本線程強制退出
            }

            pool->waitcount++;	//等待任務的線程數加一
            bool bSignal = pool->cond.timewait(pool->waitsec); //新任務等待被喚醒
            pool->waitcount--;	//沒等到,沒事幹,喝西北風了

            // 刪除無用線程
            if (!bSignal && pool->count > pool->mincount)	//如果沒事幹 && 有多餘線程
            {
                pool->count--;	//先裁員一個,不要一次做絕了,反正是在while循環裏面,沒事幹裁員機會多得是
                pool->cond.unlock();
                pthread_exit(NULL);
            }
        }
        pool->cond.unlock();	//記得要釋放鎖

//如果有工作線程在等待
        if (!pool->taskList.empty())
        {
            pool->taskCond.lock();	//上任務鎖
            Task *t = pool->taskList.front(); 	//獲取任務隊列中最前端的任務並執行
            pool->taskList.pop_front(); //移除被領取的任務
            pool->taskCond.unlock();//記得解鎖

            t->run(); //任務開始
            delete t; //弄完就刪了
        }
    }
    pthread_exit(NULL);
}

//開放接口2,向任務隊列中添加任務
void E_PThread_Pool::addTask(Task *task)
{
    if (Stop)	//線程池是否停止工作
        return;

    //向任務隊列中添加新任務
    taskCond.lock();	//上任務鎖
        taskList.push_back(task);	//添加任務
    taskCond.unlock();	//記得解鎖

    cond.lock();	//上線程鎖
    if(waitcount)	//如果有空閒線程
    {
        cond.signal();	//喚醒一個線程
    }
    else if(count < maxcount)	//如果沒有空閒線程,一般來說,走到這裏面來,那這個線程池的設計是有點失敗了	
    {
        createThread();	//那就創建一個
        cond.signal();	//然後喚醒
    }
    cond.unlock();
}


void E_PThread_Pool::destroyThread()
{
    printf("destroy?\n");

#if 0   //強行清理
    list_task::iterator it = taskList.begin();
    for (; it! = taskList.end(); it++)
    {
        Task *t = *it;
        delete t;

        t = NULL;
    }
    taskList.clear();
#endif

// 等待所有線程執行完畢
    Stop = true;
    while (count > 0)
    {
        cond.lock();
        cond.broadcast();	//廣播
        cond.unlock();
        
        sleep(1);
    }
}

其它

代碼註釋已經夠詳盡了,如果還有不明白的,可以底下評論。

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