學以至用設計模式 之 「享元模式」

在這裏插入圖片描述

開場白

以前寫「橋接模式」的時候,說「橋接模式」是最抽象的設計模式,那是由於我沒接觸到「享元模式」。
可能橋接模式是最抽象的設計模式,可是享元模式我以爲是最煩的設計模式了。前端

由於這個模式和==「池技術」有着密不可分==的聯繫。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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索