開場白
以前寫「橋接模式」的時候,說「橋接模式」是最抽象的設計模式,那是由於我沒接觸到「享元模式」。
可能橋接模式是最抽象的設計模式,可是享元模式我以爲是最煩的設計模式了。前端
由於這個模式和==「池技術」有着密不可分==的聯繫。ios
享元模式與池技術
說到享元模式,第一個想到的應該就是池技術了,String常量池、數據庫鏈接池、緩衝池等等都是享元模式的應用,因此說享元模式是池技術的重要實現方式。web
面向對象技術能夠很好地解決一些靈活性或可擴展性問題,但在不少狀況下須要在系統中增長類和對象的個數。當對象數量太多時,將致使運行代價太高,帶來性能降低等問題。享元模式正是爲解決這一類問題而誕生的。享元模式經過共享技術實現相同或類似對象的重用。數據庫
享元模式定義與結構
享元模式(Flyweight Pattern):運用共享技術有效地支持大量細粒度對象的複用。系統只使用少許的對象,而這些對象都很類似,狀態變化很小,能夠實現對象的屢次複用。因爲 享元模式要求可以共享的對象必須是細粒度對象,所以它又稱爲輕量級模式,它是一種 對象結構型模式。設計模式
線程池
內心沒底,仍是先來個線程池壓壓驚吧。安全
//pthreadpool.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 };
//pthreadpool.cpp #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); } }
調用的地方是這樣的:併發
class DoTask : public Task { public: DoTask(BtoC& send, PacketCommand1& packet); int run(); private: DB_command* task_db; BtoC* m_send; PacketCommand1 m_packet; PacketCommand3* f_packet; }; class BackServer { public: BackServer(char* IPnum); ~BackServer() { } int run(); private: PacketCommand1 m_packet; BtoC m_send; Pthread_Pool* m_pool; };
int BackServer::run() { int n = 0; while (1) { n = m_send.Read_date(m_packet.getData()); m_packet.setSize(n); DoTask* t = new DoTask(m_send, m_packet); m_pool->addTask(t); } return 0; }
在這個線程池中呢,能夠看到負責建立線程和管理線程的函數(享元工廠)、每條線程的共用屬性(外部屬性)、傳遞給每一個線程的不一樣任務(內部屬性),還有負責緩衝的任務隊列。
這些部分(享元工廠、元素外部屬性、元素內部屬性),就是享元模式的主要構成。ide
不過,在線程池調用的過程當中,確是存在了一個問題:DoTask* t = new DoTask(m_send, m_packet);這個可不見得回收了,要是等着系統的垃圾回收機制也是能夠的,可是在高併發的狀況下,這些尸位素餐的DoTask* t無疑成爲了等待資源的任務們的「公敵」。svg
那麼,今天我就來弄一個對象池,解決這個問題。函數
對象池類圖
對象公有屬性: (SignInfo)
DB_command* task_db; PacketCommand3* f_packet;
對象公有方法:
virtual int run(); virtual void setidentify(string identify); virtual string getidentify();
對象私有屬性:(SignInfoPool)
BtoC* m_send; PacketCommand1 m_packet; string identify; //身份標識 int state; //是否處於空閒態
對象私有方法:
int run(); void setidentify(string identify); string getidentify();
享元工廠屬性:
map<string,vector<SignInfo*>> mapSign; //hashmap不會用
享元工廠方法:
SignInfo* getSignInfo(string identify);
這樣可好?
畫個圖看看:
接下來代碼實現看看。
對象池代碼實現
#include<iostream> #include<map> #include<vector> #include<string> using namespace std; class SignInfo { private: int db_task; //用int替代吧 int f_packet; public: virtual int run() = 0; virtual void setidentify(string identify) = 0; virtual string getidentify() = 0; virtual int isRun() = 0; }; class SignInfoPool : public SignInfo { private: string identify; int m_send; int m_packet; int state; //是否在使用 public: SignInfoPool() { this->state = 0; } //實例化對象時使用 void setInfo(int m_send, int m_packet) { this->m_send = m_send; this->m_packet = m_packet; }; //工廠生產的時候使用 void setidentify(string identify) { this->identify = identify; } string getidentify() { return this->getidentify(); } void setState(int state) { this->state = state; } int getState() { return this->state; } int isRun() { return this->state; } //在運行返回1.沒運行返回0 int run() { cout << identify << " dosomething" << endl; } }; class SignInfoFactory { private: map<string, vector<SignInfo*>> mapSignInfo; int maxi; //最大對象數 int mini; //核心對象數 public: SignInfoFactory(int maxi,int mini) { this->maxi = maxi; this->mini = mini; createsigninfo("DBlogin"); //初始化一些用來處理登陸的對象 createsigninfo("DBregist"); //初始化一些用來處理註冊的對象 createsigninfo("DBfpwd"); //初始化一些用來處理密碼的對象 createsigninfo("DBfile"); //初始化一些用來處理文件的對象 } //初始化一些新對象 void createsigninfo(string identify) { vector<SignInfo*> temp; SignInfo* signinfo; for (int i = 0; i < mini; i++) { signinfo = new SignInfoPool(); signinfo->setidentify(identify); temp.push_back(signinfo); } mapSignInfo[identify] = temp; } SignInfo* getSignInfo(string identify) { int size = (mapSignInfo[identify]).size(); for (int i = 0; i < size; i++) { if (!(mapSignInfo[identify])[i]->isRun()) { return (mapSignInfo[identify])[i]; } } } void DestoryFactory() { //這。。。我也想知道怎麼銷燬。。。map沒有迭代器啊。。。 } //結束時的工廠銷燬 };
初次上手「享元模式」,多有紕漏,再寫之時會整合成一個類,像線程池那樣。
本文分享 CSDN - 看,將來。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。