
文章目錄
- 前言
- 觀察者模式
- 觀察者模式案例(執行緒池)
- 觀察者模式的優勢
- 注意事項
前言
關于設計模式,這次我要一改常態,我就挑重要的講,那些碎碎的就算了,
觀察者模式
說到觀察者模式,那自然是離不開執行緒了,
什么是觀察者模式呢?顧名思義,是一種觸發機制,在電視里見過埋手雷不?某個倒霉蛋不小心扯到了手雷的線,轟的一聲兒手雷炸了,倒霉蛋瞬間連渣都沒得了,
這就是觀察者模式,其中要素有:監視者、訊息傳遞、回應者,
那根線就是監視者,訊息傳遞方式為線拉動了手雷的保險栓,回應者為手雷,轟的一聲就是它的回應,
觀察者模式案例(執行緒池)
這段代碼后面還看得到,因為享元模式的一個很經典的案例也是執行緒池,,,
//Pthread_pool.h
#pragma once
#include <pthread.h>
#include <unistd.h>
#include <list> //據說list不安全,不安全就不安全吧,更不安全的都忍了
#include "Cond.h" //封裝過的條件變數類,繼承自封裝的mutex鎖類,所以具有鎖和條件變數的雙重屬性
using namespace std;
class Task //任務介面,每個任務必須實作的介面,以供作業執行緒調度任務的執行
{
public:
Task() {}
virtual ~Task() {}
virtual int run() = 0; //留給子類實作
};
typedef list<Task*> list_task; //任務佇列,用于暫存等待處理的任務,等待執行緒喚醒時處理,提供一種緩沖機制,
class Pthread_Pool //執行緒池類
{
public:
Pthread_Pool(unsigned int max = 100, unsigned int min = 10, unsigned int wait = 60);
~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; //任務佇列
Cond taskCond; //任務鎖,執行緒接任務時使用
Cond cond; //執行緒鎖,創建執行緒時使用
bool Stop; //執行緒池是否被允許運作,初始化執行緒池物件時置0,執行緒池銷毀時置為1
};
#include "Pthread_Pool.h"
//開放介面1
Pthread_Pool::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();
}
Pthread_Pool::~Pthread_Pool()
{
destroyThread(); //銷毀執行緒池
}
void 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* Pthread_Pool::taskThread(void* arg)
{
pthread_detach(pthread_self()); //設定執行緒自分離屬性
Pthread_Pool* pool = (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 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 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);
}
}
這里面還配置了保證執行緒同步的鎖,而觀察者模式的喚醒,即采用條件變數來喚醒,一旦有任務的到來,會判斷是否有空余執行緒,如果有,就直接喚醒一個去處理,如果沒有,就會加入到任務佇列中去,
觀察者模式的優勢
- 觀察者和被觀察者之間是抽象耦合的,如此設計,不論是觀察者還是被觀察者,都可以獨立拓展,
- 建立了一套觸發機制,
注意事項
- 廣播鏈問題
如果一個物件,它既是觀察者,又是被觀察者,那就比較復雜了,我是還沒遇到那種特別變態的廣播鏈了,簡單點的單行廣播鏈還是可以應付的(每條鏈都是三個物件,用”中介+觀察“就可以解決),
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/258206.html
標籤:其他
上一篇:什么是ESB架構?
下一篇:常見的微服務故障
