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

简介:

在这里插入图片描述

@[toc]

前言

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

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


享元模式与池技术

说到享元模式,第一个想到的应该就是池技术了,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
};
//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;
}

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

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

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

对象池类图

对象公有属性: (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没有迭代器啊。。。
    }    //结束时的工厂销毁
};

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

在这里插入图片描述

相关文章
|
5月前
|
设计模式 存储 Java
23种设计模式,享元模式的概念优缺点以及JAVA代码举例
【4月更文挑战第6天】享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享技术有效地支持大量细粒度对象的重用。这个模式在处理大量对象时非常有用,特别是当这些对象中的许多实例实际上可以共享相同的状态时,从而可以减少内存占用,提高程序效率
90 4
|
5月前
|
设计模式
二十三种设计模式全面解析-组合模式与享元模式的结合应用:实现对象的共享和高效管理
二十三种设计模式全面解析-组合模式与享元模式的结合应用:实现对象的共享和高效管理
|
9天前
|
设计模式 Java
Java设计模式-享元模式(12)
Java设计模式-享元模式(12)
|
2月前
|
设计模式 存储 Java
【十】设计模式~~~结构型模式~~~享元模式(Java)
文章详细介绍了享元模式(Flyweight Pattern),这是一种对象结构型模式,通过共享技术实现大量细粒度对象的重用,区分内部状态和外部状态来减少内存中对象的数量,提高系统性能。通过围棋棋子的设计案例,展示了享元模式的动机、定义、结构、优点、缺点以及适用场景,并探讨了单纯享元模式和复合享元模式以及与其他模式的联用。
【十】设计模式~~~结构型模式~~~享元模式(Java)
|
3月前
|
设计模式 存储 JavaScript
js设计模式【详解】—— 享元模式
js设计模式【详解】—— 享元模式
44 6
|
4月前
|
设计模式 缓存 Java
Java设计模式:享元模式实现高效对象共享与内存优化(十一)
Java设计模式:享元模式实现高效对象共享与内存优化(十一)
|
4月前
|
设计模式 存储 Java
Java设计模式之享元模式详解
Java设计模式之享元模式详解
|
4月前
|
设计模式
享元模式-大话设计模式
享元模式-大话设计模式
|
5月前
|
设计模式 Go
[设计模式 Go实现] 结构型~享元模式
[设计模式 Go实现] 结构型~享元模式
|
5月前
|
设计模式 Java 开发者
【搞懂设计模式】享元模式:共享节约,皆大欢喜!
【搞懂设计模式】享元模式:共享节约,皆大欢喜!
83 0