我用过的设计模式(7)--享元模式

在这里插入图片描述

前言

之前写“桥接模式”的时候,说“桥接模式”是最抽象的设计模式,那是因为我没接触到“享元模式”。
可能桥接模式是最抽象的设计模式,但是享元模式我觉得是最烦的设计模式了。

因为这个模式和“池技术”有着密不可分的联系。


享元模式与池技术

说到享元模式,第一个想到的应该就是池技术了,String常量池、数据库连接池、缓冲池、线程池等等都是享元模式的应用,所以说享元模式是池技术和池技术密不可分。

面向对象技术可以很好地解决一些灵活性或可扩展性问题,但在很多情况下需要在系统中增加类和对象的个数。当对象数量太多时,将导致运行代价过高,带来性能下降等问题。享元模式正是为解决这一类问题而诞生的。享元模式通过共享技术实现相同或相似对象的重用。

享元模式定义与结构

享元模式(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
};

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
//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); }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141

调用的地方是这样的:

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;
};

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
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;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在这个线程池中呢,可以看到负责创建线程和管理线程的函数(享元工厂)、每条线程的共用属性(外部属性)、传递给每个线程的不同任务(内部属性),还有负责缓冲的任务队列。
这些部分(享元工厂、元素外部属性、元素内部属性),就是享元模式的主要构成。

不过,在线程池调用的过程中,确是存在了一个问题:DoTask* t = new DoTask(m_send, m_packet);这个可不见得回收了,要是等着系统的垃圾回收机制也是可以的,但是在高并发的情况下,这些尸位素餐的DoTask* t无疑成为了等待资源的任务们的“公敌”。

那么,今天我就来弄一个对象池,解决这个问题。

对象池类图

对象公有属性: (SignInfo)

	DB_command* task_db; PacketCommand3* f_packet;

  
 
  • 1
  • 2

对象公有方法:

	virtual int run();
	virtual void setidentify(string identify);
	virtual string getidentify();

  
 
  • 1
  • 2
  • 3

对象私有属性:(SignInfoPool)

	BtoC* m_send; PacketCommand1 m_packet; string identify;	//身份标识 int state;	//是否处于空闲态

  
 
  • 1
  • 2
  • 3
  • 4

对象私有方法:

	int run();
	void setidentify(string identify);
	string getidentify();

  
 
  • 1
  • 2
  • 3

享元工厂属性:

	map<string,vector<SignInfo*>> mapSign;	//hashmap不会用

  
 
  • 1

享元工厂方法:

	SignInfo* getSignInfo(string identify);

  
 
  • 1

这样可好?
画个图看看:

在这里插入图片描述

接下来代码实现看看。

对象池代码实现

#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没有迭代器啊。。。
	}	//结束时的工厂销毁
};

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99

初次上手“享元模式”,多有纰漏,再写之时会整合成一个类,像线程池那样。

在这里插入图片描述

文章来源: lion-wu.blog.csdn.net,作者:看,未来,版权归原作者所有,如需转载,请联系作者。

原文链接:lion-wu.blog.csdn.net/article/details/107091678

(完)