服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C++ 实现对象池的具体方法

C++ 实现对象池的具体方法

2022-08-29 15:10Alfred-N C/C++

本文主要介绍了C++ 实现对象池的具体方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

前言

需求无限,但资源有限的情况下,就需要对资源进行专门的管理。不断的申请和释放内存是不合理的,会造成内存的波动,以及内存不受限的增长。比如,实现了一个消息队列,当发消息的速度快于处理消息的速度时,如果不对资源进行控制,就会导致内存不断的增长。除非有专门的内存管理机制,或明确的编译器优化内存复用,否则建立一个资源管理模块是很有必要的。对象池就是一个对限定数量资源复用管理的模块。

一、什么是对象池

复用对象,消除频繁的对象创建销毁带来的性能消耗,以及避免内存增长的不可控。比如,线程池、连接池都是为了实现复用对象。
举个例子:假设在生产者消费者模型中,生产者生产时创建对象,消费者消费后销毁对象。直接简单的使用new和delete,就会让对象频繁创建和销毁导致额外性能消耗,而且生产者速度大于消费者速度时,就会让对象数量创建大于销毁导致内存不受控制增长。如果使用对象池,就可以让生产和消费复用固定数量的对象,很好的避免了频繁创建销毁对象以及内存增长不受控制的情况。

二、如何实现

1.确定接口

(1)、确定动态关系
通过序列图可以确定对象需要的接口,我们以socket服务为场景绘制序列图,如下

C++ 实现对象池的具体方法

(2)、确定静态关系
根据上面的序列图确定的接口绘制成类图,如下:

C++ 实现对象池的具体方法

2.转成代码

由于模块规模小,接口也不多,所以就不展示进一步细化设计了。因为本文讲述的是C++实现对象池,所以将上述设计转化为C++接口定义。如下:

?
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
/// <summary>
/// 对象池
/// </summary>
class ObjectPool
{
public:    
    /// <summary>
    /// 构造方法
    /// </summary>
    /// <param name="bufferArray">对象池的缓冲区,由外部指定,可以理解为一个数组。数组大小需满足bufferSize>=elementSize*arraySize</param>
    /// <param name="elementSize">数组元素大小</param>
    /// <param name="arraySize">数组长度或元素个数</param>
    ObjectPool(void* bufferArray, int elementSize, int arraySize );    
    /// <summary>
    /// 申请对象
    /// 如果池里对象不足,则会等待,直到有对象才返回。
    /// </summary>
    /// <returns>返回申请的对象指针</returns>
    void* Applicate();
    /// <summary>
    /// 申请对象
    /// </summary>
    /// <param name="timeout">超时时间,超时后返回null</param>
    /// <returns>返回申请的对象指针</returns>
    void* Applicate(int timeout);  
    /// <summary>
    /// 归还对象
    /// </summary>
    /// <param name="element">需归还的对象</param>
    void ReturnBack(void* element);
};

三、完整代码

根据上述的初步设计,再进行细化,以及实现,最终得出如下代码实现。
ObjectPool.h

?
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
#ifndef OBJECTPOOL_H
#define OBJECTPOOL_H
/************************************************************************
* @Project:     AC::ObjectPool
* @Decription:  对象池:“需求很大,但数量有限”的情况下,就需要对资源进行专门的管理,
*不断的申请和释放对象是不合理的(除非有专门的内存管理机制,或明确的编译优化内存复用)。
*这是一个对限定数量资源的复用管理模块。
* @Verision:    v1.0.0.1
* @Author:      Xin Nie
* @Create:      2018/12/21 13:34:00
* @LastUpdate:  2022/1/5 13:53:00
************************************************************************
* Copyright @ 2022. All rights reserved.
************************************************************************/
#include<unordered_map>
#include<vector>
#include<mutex>
#include<condition_variable>
namespace AC {
    /// <summary>
    /// 对象池
    /// </summary>
    class ObjectPool
    {
    public:    
        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="bufferArray">对象池的缓冲区,由外部指定,可以理解为一个数组。数组大小需满足bufferSize>=elementSize*arraySize</param>
        /// <param name="elementSize">数组元素大小</param>
        /// <param name="arraySize">数组长度或元素个数</param>
        ObjectPool(void* bufferArray, int elementSize, int arraySize );
        /// <summary>
        /// 析构方法
        /// </summary>
        ~ObjectPool();
        /// <summary>
        /// 申请对象
        /// 如果池里对象不足,则会等待,直到有对象才返回。
        /// </summary>
        /// <returns>返回申请的对象指针</returns>
        void* Applicate();
        /// <summary>
        /// 申请对象
        /// </summary>
        /// <param name="timeout">超时时间,超时后返回null</param>
        /// <returns>返回申请的对象指针</returns>
        void* Applicate(int timeout);  
        /// <summary>
        /// 归还对象
        /// </summary>
        /// <param name="element">需归还的对象</param>
        void ReturnBack(void* element);
        /// <summary>
        /// 获取对象池的缓冲区,即构造方法中的bufferArray
        /// </summary>
        /// <returns>缓冲区的指针</returns>
        void* GetPoolBuffer();
        /// <summary>
        /// 获取对象的大小,即构造方法中的elementSize
        /// </summary>
        /// <returns>对象的大小</returns>
        int GetObjectSize();
        /// <summary>
        /// 获取总的对象数量,即构造方法中的arraySize
        /// </summary>
        /// <returns>总的对象数量</returns>
        int GetObjectCount();
    private:
        void*_buffer = NULL;
        int _elementSize;
        int _arraySize;
        std::vector<void*> _unusedUnits;
        std::unordered_map<void*, int> _usedUnits;
        std::mutex _mutex;
        std::condition_variable _cond;
    };
 
    /// <summary>
    /// 泛型对象池
    /// </summary>
    /// <typeparam name="T">对象类型</typeparam>
    template<typename T>
    class ObjectPoolGeneric:private ObjectPool
    {
    public:
        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="array">对象数组</param>
        /// <param name="size">数组大小</param>
        /// <returns></returns>
        ObjectPoolGeneric(T*array,int size) :ObjectPool(array, sizeof(T), size)
        {
        }
        /// <summary>
        /// 析构方法
        /// </summary>
        ~ObjectPoolGeneric() {}
        /// <summary>
        /// 申请对象
        /// 如果池里对象不足,则会等待,直到有对象才返回。
        /// </summary>
        /// <returns>返回申请的对象指针</returns>
        T* Applicate() {
            return (T*)ObjectPool::Applicate();
        }
        /// <summary>
        /// 申请对象
        /// </summary>
        /// <param name="timeout">超时时间,超时后返回null</param>
        /// <returns>返回申请的对象指针</returns>
        T* Applicate(int timeout) {
            return (T*)ObjectPool::Applicate(timeout);
        }
        /// <summary>
        /// 归还对象
        /// </summary>
        /// <param name="element">需归还的对象</param>
        void ReturnBack(T* element)
        {
            ObjectPool::ReturnBack(element);
        }
        /// <summary>
        /// 获取对象池的缓冲区,即构造方法中的bufferArray
        /// </summary>
        /// <returns>缓冲区的指针</returns>
        T* GetPoolBuffer() {
            return (T*)ObjectPool::GetPoolBuffer();
        }
    };
}
#endif

ObjectPool.cpp

?
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
#include "ObjectPool.h"
#include <chrono>
namespace AC {
    ObjectPool::ObjectPool(void* bufferArray, int elementSize, int arraySize)
    {
        if (elementSize < 1 || arraySize < 1)
            return;
        _buffer = bufferArray;
        _elementSize = elementSize;
        _arraySize = arraySize;
        char* firstAdress = (char*)bufferArray;
        //记录未使用的索引
        for (int i = 0; i < arraySize; i++)
        {
            _unusedUnits.push_back(&(firstAdress[i * elementSize]));
        }
    }
    ObjectPool::~ObjectPool()
    {
    }
    void* ObjectPool::Applicate()
    {
        return Applicate(-1);
    }
    void* ObjectPool::Applicate(int timeout) {
        void* resource = NULL;
        std::unique_lock<std::mutex> l(_mutex);
        while (_unusedUnits.size() < 1)
        {
            if (timeout == -1)
            {
                _cond.wait(l);
            }
            else if (_cond.wait_for(l, std::chrono::microseconds(timeout)) == std::cv_status::timeout)
            {
                return nullptr;
            }
        }
        resource = _unusedUnits.back();
        _usedUnits[resource] = 1;
        _unusedUnits.pop_back();
        return resource;
    }
    void ObjectPool::ReturnBack(void* element) {
        _mutex.lock();
        auto iter = _usedUnits.find(element);
        if (iter != _usedUnits.end())
        {
            _unusedUnits.push_back(element);
            _usedUnits.erase(iter);
            _cond.notify_one();
        }
        _mutex.unlock();
    }
    void* ObjectPool::GetPoolBuffer()
    {
        return _buffer;
    }
    int ObjectPool::GetObjectSize()
    {
        return _elementSize;
    }
    int ObjectPool::GetObjectCount()
    {
        return _arraySize;
    }
}

四、使用示例

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
#include "ObjectPool.h"
class A {
public:
    A() {
        printf("%p\n", this);
    }
};
int main(int argc, char** argv) {
    //初始化对象池,2个对象
    AC::ObjectPool objectPool(new char[sizeof(A) * 2], sizeof(A), 2);
    A* a, * b, * c;
    //申请对象,使用定位new初始化对象
    a = new (objectPool.Applicate())A;
    b = new (objectPool.Applicate())A;
    //归还对象
    a->~A();//返初始化对象
    objectPool.ReturnBack(a);
    c = new (objectPool.Applicate())A;
    b->~A();
    c->~A();
    //使用结束,删除缓存
    delete  objectPool.GetPoolBuffer();
    return 0;
}

输出:
016502E9
016502E8
016502E9

2、简易的线程池,示例:

?
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
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include "ObjectPool.h"
class ThreadInfo {
public:
    std::thread* pThread;
    std::mutex _mutext;
    std::condition_variable _cv;
    std::function<void()> _threadPoc;
};
//线程信息数组,数组长度即线程池的线程数
ThreadInfo _threadArray[3];
//对象池,使用线程信息数组初始化
AC::ObjectPoolGeneric<ThreadInfo>_threadPool(_threadArray, 3);
bool _exitThreadPool = false;
//在线程池中运行方法
void RunInThreadPool(std::function<void()> f) {
    //申请线程
    auto threadInfo = _threadPool.Applicate();
    threadInfo->_threadPoc = f;
    if (threadInfo->pThread)
        //复用线程
    {
        threadInfo->_cv.notify_one();
    }
    else
        //创建线程
    {
        threadInfo->pThread = new std::thread([=]() {
            while (!_exitThreadPool)
            {
                printf("thread %d run\n", threadInfo->pThread->get_id());
                if (threadInfo->_threadPoc)
                {   //执行线程操作
                    threadInfo->_threadPoc();
                }
                printf("thread %d stop\n", threadInfo->pThread->get_id());
                //归还线程
                _threadPool.ReturnBack(threadInfo);
                std::unique_lock<std::mutex>lck(threadInfo->_mutext);
                threadInfo->_cv.wait(lck);
            }
        });
    }
}
int main(int argc, char** argv) {
    while(true)
    {   //在线程池中运行方法
        RunInThreadPool([]() {     
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));  
        });
    }
    return 0;
}

输出:
thread 69664 run
thread 57540 run
thread 56876 run
thread 69664 stop
thread 69664 run
thread 57540 stop
thread 56876 stop
thread 57540 run
thread 56876 run
thread 69664 stop
thread 69664 run
thread 56876 stop
thread 57540 stop
thread 56876 run
thread 57540 run
thread 69664 stop

总结

以上就是今天要讲的内容,本文介绍了对象池的设计与实现以及使用,其使用场景其实不算多,因为很多需要对象复用的场景通常以及有底层实现了,比如线程池数据库的连接池等,所以本文讲的内容只能适用于少数的场景,比如waveOut播放音频时是可以使用对象池实现 的。但总得来说,对象池还是有用的,所以将其写成博客用于记录曾经用过的技术。

到此这篇关于C++ 实现对象池的具体方法的文章就介绍到这了,更多相关C++ 对象池内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/u013113678/article/details/122314080

延伸 · 阅读

精彩推荐
  • C/C++C++关于指针,继承和多态介绍

    C++关于指针,继承和多态介绍

    大家好,本篇文章主要讲的是C++关于指针,继承和多态介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览...

    山顶的小道士8792022-08-11
  • C/C++C语言基于循环链表解决约瑟夫环问题的方法示例

    C语言基于循环链表解决约瑟夫环问题的方法示例

    这篇文章主要介绍了C语言基于循环链表解决约瑟夫环问题的方法,简单描述了约瑟夫环问题并结合实例形式分析了C语言使用循环链表解决约瑟夫环问题的具...

    handsome_ZHANG3722021-06-11
  • C/C++C语言循环结构详解

    C语言循环结构详解

    本文主要介绍C语言循环结构的基础知识,这里整理了循环的基础资料并附简单的代码示例详细讲解,有需要的小伙伴可以参考下...

    For forever8322022-01-20
  • C/C++C++实现LeetCode(102.二叉树层序遍历)

    C++实现LeetCode(102.二叉树层序遍历)

    这篇文章主要介绍了C++实现LeetCode(102.二叉树层序遍历),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...

    Grandyang7332021-12-01
  • C/C++C语言实现弹跳小球

    C语言实现弹跳小球

    这篇文章主要为大家详细介绍了C语言实现弹跳小球,设置了小球分数及过关难度,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们...

    beamstar11082021-08-26
  • C/C++c++非变易算法-stl算法

    c++非变易算法-stl算法

    本文主要介绍了C++ STL算法库中的非变易算法,是一些原则上不会变更操作数据的算法,包括:逐个查找算法、元素搜索算法、元素统计算法、序列匹配算法...

    C++教程网6202021-01-17
  • C/C++C++实现八皇后问题的方法

    C++实现八皇后问题的方法

    这篇文章主要介绍了C++实现八皇后问题的方法,是数据结构与算法中常见的一个经典算法,需要的朋友可以参考下...

    C++教程网9942021-01-30
  • C/C++C++中可以接受任意多个参数的函数定义方法(详解)

    C++中可以接受任意多个参数的函数定义方法(详解)

    下面小编就为大家带来一篇C++中可以接受任意多个参数的函数定义方法(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编...

    C++教程网11222021-04-19