开场白
之前写“桥接模式”的时候,说“桥接模式”是最抽象的设计模式,那是因为我没接触到“享元模式”。 可能桥接模式是最抽象的设计模式,但是享元模式我觉得是最烦的设计模式了。
因为这个模式和==“池技术”有着密不可分==的联系。
享元模式与池技术
说到享元模式,第一个想到的应该就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,所以说享元模式是池技术的重要实现方式。
面向对象技术可以很好地解决一些灵活性或可扩展性问题,但在很多情况下需要在系统中增加类和对象的个数。当对象数量太多时,将导致运行代价过高,带来性能下降等问题。享元模式正是为解决这一类问题而诞生的。享元模式通过共享技术实现相同或相似对象的重用。
享元模式定义与结构
享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于 享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种 对象结构型模式。
线程池
心里没底,还是先来个线程池压压惊吧。
代码语言:javascript复制//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
};
代码语言:javascript复制//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);
}
}
调用的地方是这样的:
代码语言:javascript复制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;
};
代码语言:javascript复制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;
}
在这个线程池中呢,可以看到负责创建线程和管理线程的函数(享元工厂)、每条线程的共用属性(外部属性)、传递给每个线程的不同任务(内部属性),还有负责缓冲的任务队列。 这些部分(享元工厂、元素外部属性、元素内部属性),就是享元模式的主要构成。
不过,在线程池调用的过程中,确是存在了一个问题:DoTask* t = new DoTask(m_send, m_packet);这个可不见得回收了,要是等着系统的垃圾回收机制也是可以的,但是在高并发的情况下,这些尸位素餐的DoTask* t无疑成为了等待资源的任务们的“公敌”。
那么,今天我就来弄一个对象池,解决这个问题。
对象池类图
对象公有属性: (SignInfo)
代码语言:javascript复制 DB_command* task_db;
PacketCommand3* f_packet;
对象公有方法:
代码语言:javascript复制 virtual int run();
virtual void setidentify(string identify);
virtual string getidentify();
对象私有属性:(SignInfoPool)
代码语言:javascript复制 BtoC* m_send;
PacketCommand1 m_packet;
string identify; //身份标识
int state; //是否处于空闲态
对象私有方法:
代码语言:javascript复制 int run();
void setidentify(string identify);
string getidentify();
享元工厂属性:
代码语言:javascript复制 map<string,vector<SignInfo*>> mapSign; //hashmap不会用
享元工厂方法:
代码语言:javascript复制 SignInfo* getSignInfo(string identify);
这样可好? 画个图看看:
接下来代码实现看看。
对象池代码实现
代码语言:javascript复制#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没有迭代器啊。。。
} //结束时的工厂销毁
};
初次上手“享元模式”,多有纰漏,再写之时会整合成一个类,像线程池那样。