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

网友投稿 581 2022-05-30

文章目录

前言

享元模式与池技术

享元模式定义与结构

线程池

对象池类图

对象池代码实现

前言

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

可能桥接模式是最抽象的设计模式,但是享元模式我觉得是最烦的设计模式了。

因为这个模式和“

池技术

”有着

密不可分

的联系。

享元模式与池技术

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

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

享元模式定义与结构

享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于 享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种 对象结构型模式。

线程池

心里没底,还是先来个线程池压压惊吧。

//pthreadpool.h #pragma once #include #include #include //据说list不安全,不安全就不安全吧,更不安全的都忍了 #include "Cond.h" //封装过的条件变量类,继承自封装的mutex锁类,所以具有锁和条件变量的双重属性 using namespace std; class Task //任务接口,每个任务必须实现的接口,以供工作线程调度任务的执行 { public: Task() {} virtual ~Task() {} virtual int run() = 0; //留给子类实现 }; typedef list 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

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

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> mapSign; //hashmap不会用

1

享元工厂方法:

SignInfo* getSignInfo(string identify);

1

这样可好?

画个图看看:

接下来代码实现看看。

对象池代码实现

#include #include #include #include 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> 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 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

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

任务调度

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:累足成步 华为云发布鲲鹏云服务加速企业创新升级
下一篇:细说HTTP-request
相关文章