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)沒有任務了線程退出。