使用C/C++實現內存池技術

使用C/C++實現內存池技術

內存管理技術是開發多媒體應用和服務的很重要的知識。DMSP應用中會有頻繁的緩衝區的創建和釋放操作,這些操作會降低程序的運行效率和運行時間。本節在講解內存池技術的同時,講解對象的創建和使用方法。

內存池技術主要的思想是:被創建的緩衝區,在使用完後,並不立即釋放,而是存放在一個空閒隊列池中;當程序需要新的緩衝區時,首先從內存池中獲取可用的緩衝區;在內存池中沒有可用的緩衝區時,纔去創建新的。通過這種內存池方法,可以減少頻繁的緩衝區的創建和釋放操作,減少內存碎片的發生,從而較有效的管理緩衝區。

內存池技術中,每一塊內存被當作一個內存對象(IXBufferItem),這些對象可以管理自已的生成周期;同時,結合內存管理器對象(IXBufferManager)的管理功能,在內存對象需要被釋放時,調用內存管理對象的相應接口(Reuse)通知管理對象自已需要被釋放,訊問是否被再次利用,並根據返回的情況決定是釋放自已,還是其他操作。

 

內存對象及其內存池管理對象被定義在下表中的源代碼文件中。x_buffer_manager.h定義了內存對象和內存池管理對象,還定義了相關的操作函數組。

源文件x_buffer_manager.h


// x_buffer_manager.h: 緩衝區結點及其管理定義;
#ifndef _x_buffer_manager_h_
#define _x_buffer_manager_h_
 
#include "../include/x_nb_baseobject.h"
 
//緩衝區對象;
class IXBufferItem : public ISelfBaseObject
{
public:
virtual    unsigned long __stdcall GetLength(unsigned long* pdwLength) = 0;
virtual    unsigned long __stdcall SetLength(unsigned long dwLength) = 0;
virtual    unsigned long __stdcall GetMaxLength(unsigned long* pdwLength) = 0;  
virtual    unsigned long __stdcall GetBuffer(unsigned char** ppdwBuffer) = 0;
virtual    unsigned long __stdcall GetBufferAndLength(unsigned char** ppdwBuffer,  unsigned long *pdwLength) = 0;
};
 
//緩衝區管理對象;
class IXBufferManage : public ISelfBaseObject
{
public:
virtual    unsigned long __stdcall Init(long inMaxCount) = 0;
virtual    unsigned long __stdcall Uninit(void) = 0;
 
virtual    unsigned long __stdcall GetEmptySample(unsigned long inRequiredSize, IXBufferItem** outBuffer) = 0;
virtual    unsigned long __stdcall Reuse(IXBufferItem* inSample) = 0;
 
};
 
#ifdef __cplusplus
extern "C" {
#endif
 
// CreateBufferItem: 創建緩衝區對象;
HRESULT __stdcall CreateBufferItem(LPVOID* lppvObj);
 
// CreateBufferManager: 創建緩衝區管理對象;
HRESULT __stdcall CreateBufferManager(LPVOID* lppvObj);
 
#ifdef __cplusplus
}
#endif
 
#endif    //(!_x_buffer_manager_h_)

 

IXBufferItemIXBufferManage都從ISelfBaseObject派生而來。還應注意上例源代碼中,使用虛函數和虛基類定義對象及其接口的方法。在實現這兩個對象時,需要創建他們的實例;這兩個函數的實例分別定義在下列源代碼文件中。

源代碼文件:buffmgr.h

// buffmgr: 緩衝區對象及其管理接口組;
#ifndef _buffmgr_h_
#define _buffmgr_h_
 
#include "../include/x_buffer_manager.h"
……
#endif    //(!_buffmgr_h_)

 

源代碼文件:buffer_item.h

// buffer_item: 緩衝區對象接口組;
#ifndef _buffer_item_h_
#define _buffer_item_h_
 
#include "buffmgr.h"
class CXBufferManage;
 
// CXBufferItem: 緩衝區對象接口組;
class CXBufferItem : public IXBufferItem
{
public: //ISelfBaseObject接口;
virtual    HRESULT      __stdcall QueryInterface(REFIID iid,
LPVOID *lppvObject);
virtual    ULONG  __stdcall AddRef(VOID);
virtual    ULONG  __stdcall Release(VOID);
virtual    DWORD __stdcall GetVersion(LPDWORD pdwHigh);
 
public: //IXBufferItem接口;
virtual    unsigned long __stdcall GetLength(unsigned long* pdwLength);
virtual    unsigned long __stdcall SetLength(unsigned long dwLength);
virtual    unsigned long __stdcall GetMaxLength(unsigned long* pdwLength);  
virtual    unsigned long __stdcall GetBuffer(unsigned char** ppdwBuffer);
virtual    unsigned long __stdcall GetBufferAndLength(unsigned char** ppdwBuffer, unsigned long *pdwLength);
 
public:
CXBufferItem(unsigned long inSize, CXBufferManage* pSmpMgr);
CXBufferItem();
~CXBufferItem();
 
public:
unsigned long       SetBufferManage(CXBufferManage* pSmpMgr);
unsigned long       VerifyBufferSize(unsigned long inRequiredSize);
 
public:
CXBufferItem*    m_pNext;       //後一個對象;
 
private:
long                 m_cRef;         //引用的計數;
unsigned char*     m_pBuffer;     //緩衝區地址;
unsigned long       m_dwBufSize;       //緩衝區長度;
unsigned long       m_dwDataSize;      //數據的長度;
 
CXBufferManage*       m_pSmpAdmin;     //緩衝的管理;
};
 
#endif    //(!_buffer_item_h_)


源代碼文件:buffer_manage.h


// buffer_manage.h: 緩衝區對象管理接口組;
#ifndef _buffer_manage_h_
#define _buffer_manage_h_
 
#include "buffmgr.h"
class CXBufferItem;
 
// CXBufferManage: 緩衝區對象管理接口組;
class CXBufferManage : public IXBufferManage
{
public: //ISelfBaseObject接口;
virtual    HRESULT      __stdcall QueryInterface(REFIID iid,
LPVOID *lppvObject);
virtual    ULONG  __stdcall AddRef(VOID);
virtual    ULONG  __stdcall Release(VOID);
virtual    DWORD __stdcall GetVersion(LPDWORD pdwHigh);
 
public: //IXBufferManage接口;
virtual    unsigned long __stdcall Init(long inMaxCount);
virtual    unsigned long __stdcall Uninit(void);
 
virtual    unsigned long __stdcall GetEmptySample(unsigned long inRequiredSize, IXBufferItem** outBuffer);
virtual    unsigned long __stdcall Reuse(IXBufferItem* inSample);
 
public:
CXBufferManage();
~CXBufferManage();
 
private:
long                        m_cRef;                //引用計數;
CXBufferItem*           m_pIdleHead;         //空閒隊列頭;
CXBufferItem*           m_pIdleTail;          //空閒隊列尾;
long                        m_nMaxCount;             //最大個數;
long                        m_nCurCount;              //當前個數;
……
};
……
#endif //(!_buffer_manage_h_)

 

CXBufferItem實現了IXBufferItem的所有的虛擬函數接口,同時在其對象內部保存了一個緩衝區對象。其中m_pBuffer指向緩衝區的首地址,m_dwBufSize是緩衝區的長度,m_dwDataSize是緩衝區中的數據長度;同時,m_pSmpAdmin是內存對象的管理器,當內存對象需要釋放時,首先檢查該對象是否存在,如果存在,則調用該對象的Reuse接口通知它回收回已;如果管理器對象不存在或者不回收自己,則CXBufferItem釋放直接釋放自己,這通過它的接口Release來實現。該類還有一個成員是m_pNext,它被內存池管理器對象使用。類CXBufferItem的其它對象主要是便於緩衝區的管理。具體的實現見下例中的源代碼。

源代碼文件:buffer_item.cpp


// buffer_item: 緩衝區結點函數組;

#include "buffer_item.h"

#include "buffer_manage.h"

 

// ISelfBaseObject接口函數組;

HRESULT    __stdcall CXBufferItem::QueryInterface(REFIID iid,

LPVOID *lppvObject)

{

     *lppvObject = reinterpret_cast<ISelfBaseObject*>(this);

     this->AddRef();

     return S_OK;

}

 

ULONG __stdcall CXBufferItem::AddRef(VOID)

{

     return ::InterlockedIncrement(&m_cRef);

}

 

ULONG __stdcall CXBufferItem::Release(VOID)

{

     if(0 == ::InterlockedDecrement(&m_cRef))

     {

            if(m_pSmpAdmin != NULL)

            {

                   //調用管理器回收緩衝區;

                   m_pSmpAdmin->Reuse(this);

            }

            else  {

                   //釋放緩衝區;

                   delete this;

            }

            return      0;

     }

     return      (ULONG)m_cRef;

}

 

DWORD      __stdcall CXBufferItem::GetVersion(LPDWORD pdwHigh)

{

     if(pdwHigh != NULL)

     {

            *pdwHigh = 0x00000001;

     }

     return      0x00010000;

}

 

//構造函數(傳入尺寸和管理對象);

CXBufferItem::CXBufferItem(unsigned long inSize,

CXBufferManage* pSmpMgr)

{

     m_cRef = 0;

 

     m_dwBufSize  = 0;

     m_dwDataSize = 0;

     m_pBuffer  = new unsigned char[inSize];

     if(m_pBuffer != NULL)

     {

            m_dwBufSize  = inSize;

            m_dwDataSize = inSize;

     }

     m_pSmpAdmin = pSmpMgr;

     if(m_pSmpAdmin != NULL)

     {

            m_pSmpAdmin->AddRef();

     }

     m_pNext = NULL;

}

 

//缺省構造函數;

CXBufferItem::CXBufferItem()

{

     m_pBuffer = NULL;

     m_dwBufSize = 0;

     m_dwDataSize = 0;

     m_pSmpAdmin = NULL;

     m_pNext = NULL;

}

 

//析構函數;

CXBufferItem::~CXBufferItem()

{

     //釋放緩衝區;

     if(m_pBuffer != NULL)

     {

            delete[] m_pBuffer;

     }

     m_pBuffer = NULL;

     //釋放對管理對象的引用;

     if(m_pSmpAdmin != NULL)

     {

            m_pSmpAdmin->Release();

     }

     m_pSmpAdmin = NULL;

}

 

//設置管理對象;

unsigned long CXBufferItem::SetBufferManage(CXBufferManage* pSmpMgr)

{

     //釋放原有的管理對象(如果有);

     if(m_pSmpAdmin != NULL)

     {

            m_pSmpAdmin->Release();

     }

     //保存管理對象(增加引用計數);

     m_pSmpAdmin = pSmpMgr;

     if(m_pSmpAdmin != NULL)

     {

            m_pSmpAdmin->AddRef();

     }

     return      0;

}

 

//調用緩衝區的大小(在原緩衝區較小時);

unsigned long CXBufferItem::VerifyBufferSize(unsigned long inRequiredSize)

{   

     unsigned long  hr = 0;

 

     if(inRequiredSize > m_dwBufSize)

     {

            if(m_pBuffer != NULL)

            {

                   delete[] m_pBuffer;

                   m_pBuffer = NULL;

            }

            m_pBuffer = new unsigned char[inRequiredSize];

            if(m_pBuffer != NULL)

            {

                   m_dwBufSize  = inRequiredSize;

                   m_dwDataSize = inRequiredSize;

                   hr = 0;

            }

            else  {

                   m_dwBufSize  = NULL;

                   m_dwDataSize = NULL;

                   hr = -1;

            }

     }

return      hr;

}

 

//獲取緩衝區中數據的長度;

unsigned long __stdcall CXBufferItem::GetLength(unsigned long *pdwLength)

{

     *pdwLength = m_dwDataSize;

     return 0;

}

 

//設置緩衝區中數據的長度;

unsigned long  __stdcall CXBufferItem::SetLength(unsigned long dwLength)

{

     m_dwDataSize = dwLength;

     return 0;

}

 

//獲取緩衝區的總長度;

unsigned long  __stdcall CXBufferItem::GetMaxLength(unsigned long *pdwLength)

{

     *pdwLength = m_dwBufSize;

     return 0;

}

 

//獲取緩衝區的地址;

unsigned long __stdcall CXBufferItem::GetBuffer(unsigned char **ppdwBuffer)

{

     *ppdwBuffer = m_pBuffer;

     return 0;

}

 

//獲取緩衝區的地址和數據的長度;

unsigned long  __stdcall CXBufferItem::GetBufferAndLength(unsigned char **ppdwBuffer, unsigned long *pdwLength)

{

     *ppdwBuffer = m_pBuffer;

     *pdwLength  = m_dwDataSize;

     return 0;

}

 

CXBufferManage實現了IXBufferManage的所有的虛擬函數接口,同時在其對象內部設置一個單向鏈表用於存儲空閒內存對象。m_pIdleHead指向該表的表頭;m_pIdleTail指向該表的表尾;m_nMaxCount設置最大可以保存的空閒對象的個數;m_nCurCount保存當前空閒對象的個數。類CXBufferManage的接口Init用於設置最大可以保存的空閒對象的個數;在該類CXBufferManage的實例在釋放時,會調用Uninit釋放所有的被保存的空閒內存對象。

調用者調用該類CXBufferManageGetEmptySample接口獲取內存對象;GetEmptySample接口首先從空閒隊列中獲取內存對象;如果沒有可用的空閒對象,則創建新的內存對象;當內存對象IXBufferItem不再使用時,調用CXBufferManageReuse接口,該接口首先檢查空閒內存對象是否達到最大值,如果沒有,則把傳入的對象加入到空閒隊列中;如果達到最大值,則不加入到隊列中。類CXBufferManage的實現見下例中的源代碼。

源代碼文件:buffer_manage.cpp


……

// buffer_manage.cpp: 緩衝區對象管理接口組;

#include "buffer_item.h"

#include "buffer_manage.h"

 

// ISelfBaseObject接口函數組;

HRESULT    __stdcall CXBufferManage::QueryInterface(REFIID iid,

LPVOID *lppvObject)

{

     *lppvObject = reinterpret_cast<ISelfBaseObject*>(this);

     this->AddRef();

     return S_OK;

}

 

ULONG __stdcall CXBufferManage::AddRef(VOID)

{

     return ::InterlockedIncrement(&m_cRef);

}

 

ULONG __stdcall CXBufferManage::Release(VOID)

{

     if(0 == ::InterlockedDecrement(&m_cRef))

     {

            delete this;

            return      0;

     }

     return      (ULONG)m_cRef;

}

 

DWORD      __stdcall CXBufferManage::GetVersion(LPDWORD pdwHigh)

{

     if(pdwHigh != NULL)

     {

            *pdwHigh = 0x00000001;

     }

     return      0x00010000;

}

 

//構造函數;

CXBufferManage::CXBufferManage()

{

     m_cRef = 1;

     m_nMaxCount = 10;

     m_nCurCount = 0;

     m_pIdleHead = NULL;

     m_pIdleTail = NULL;

}

 

//析構函數(釋放所有管理的對象);

CXBufferManage::~CXBufferManage()

{

     Uninit();

}

 

//初始化管理器(指明最大個數);

unsigned long __stdcall CXBufferManage::Init(long inMaxCount)

{

     if(inMaxCount > m_nMaxCount)

     {

            m_nMaxCount = inMaxCount;

     }

     return      1;

}

 

//釋放管理器的信息;

unsigned long __stdcall CXBufferManage::Uninit(void)

{

     CXBufferItem*      pSample = NULL;

     CXBufferItem*      pTmp = NULL;

 

     //釋放管理的所有緩衝區對象;

     pSample = m_pIdleHead;

     while(pSample)

     {

            pTmp = pSample->m_pNext;

            pSample->SetBufferManage(NULL);

            pSample->Release();

            pSample = pTmp;

     }

     m_pIdleHead = NULL;

     m_pIdleTail = NULL;

     m_nCurCount = 0;

 

     return      1;

}

 

//創建或獲取緩衝區對象;

unsigned long  __stdcall CXBufferManage::GetEmptySample(unsigned long inRequiredSize, IXBufferItem** outBuffer)

{

     CXBufferItem*      pSample = NULL;

 

     //檢查參數;

     if(!outBuffer) return 0;

     *outBuffer = NULL;

 

     //創建新的緩衝區對象(如果沒有);

     if(m_pIdleHead == NULL)

     {

            //創建新對象;

            pSample = new CXBufferItem(inRequiredSize, this);

            if(pSample == NULL)

            {

                   return 0;

            }

            pSample->VerifyBufferSize(inRequiredSize);

     }

     else  {

            pSample = m_pIdleHead;

            pSample->AddRef();

            //調整表;

            m_pIdleHead = m_pIdleHead->m_pNext;

            if(m_pIdleHead == NULL)

            {

                   m_pIdleTail = NULL;

            }

            m_nCurCount --;

     }

 

     //調整結點信息;

     pSample->VerifyBufferSize(inRequiredSize);

     *outBuffer = (IXBufferItem*)pSample;

     return      1;

}

 

//回收緩衝區對象(直接加入到空閒表中);

unsigned long  __stdcall CXBufferManage::Reuse(IXBufferItem* inSample)

{

     CXBufferItem*      pSam = NULL;

 

     //檢查參數;

     if(!inSample) return 0;

     pSam = (CXBufferItem*)inSample;

     pSam->m_pNext = NULL;

 

     //結點嘗試加入到表的尾部;

     if(m_nCurCount < m_nMaxCount)

     {

            if(!m_pIdleTail)

            {

                   m_pIdleHead = pSam;

                   m_pIdleTail = pSam;

            }

            else  {

                   m_pIdleTail->m_pNext = pSam;

                   m_pIdleTail = pSam;

            }

            m_nCurCount ++;

            return 1;

     }

     return      0;

}

 

對象IXBufferItemIXBufferManage可以保存在DLL動態庫文件中,本例通過輸出函數接口CreateBufferItemCreateBufferManager來創建這兩個對象;同時,通過相應的DEF文件來設置輸出的接口。實現細節見下表中的源代碼。

源代碼文件:buffmgr.cpp


// buffmgr: 緩衝區對象及其管理接口組;

#include "buffmgr.h"

#include "buffer_item.h"

#include "buffer_manage.h"

……

//全局變量;

HINSTANCE       ghInstance = NULL;      //庫的實例句柄;

 

//動態庫輸出主函數;

BOOL APIENTRY DllMain( HANDLE hModule,

DWORD  ul_reason_for_call, LPVOID lpReserved)

{

     switch (ul_reason_for_call)

     {

            case DLL_PROCESS_ATTACH:

            {

                   ghInstance = (HINSTANCE)hModule;

                   DisableThreadLibraryCalls(ghInstance);

                   break;

            }

            case DLL_PROCESS_DETACH:

            {

                   break;

            }

     }

     return      TRUE;

}

 

// CreateBufferItem: 創建緩衝區對象;

HRESULT __stdcall CreateBufferItem(LPVOID* lppvObj)

{

     CXBufferItem*      lpirc = NULL;

     HRESULT             hr = S_OK;

 

     //創建一個對象;

     lpirc = new CXBufferItem;

     if(lpirc == NULL)

     {

            return E_OUTOFMEMORY;

     }

 

     //查尋對象以獲取接口;

     hr = lpirc->QueryInterface(IID_ISelfBaseObject, lppvObj);

     lpirc->Release();

 

     //返回處理結果;

     return hr;

}

 

// CreateBufferManager: 創建緩衝區管理對象;

HRESULT __stdcall CreateBufferManager(LPVOID* lppvObj)

{

     CXBufferManage*  lpirc = NULL;

     HRESULT             hr = S_OK;

 

     //創建一個對象;

     lpirc = new CXBufferManage;

     if(lpirc == NULL)

     {

            return E_OUTOFMEMORY;

     }

 

     //查尋對象以獲取接口;

     hr = lpirc->QueryInterface(IID_ISelfBaseObject, lppvObj);

     lpirc->Release();

 

     //返回處理結果;

     return hr;

}

……

 

源代碼文件:buffmgr.def


……

EXPORTS

CreateBufferItem  @1

CreateBufferManager   @2

……

 

對象IXBufferItemIXBufferManage保存在DLL動態庫文件(buffmgr.dll)中,其關聯文件存放在buffmgr.lib文件中。使用該對象庫時,可以使用顯示調用方法,也可以使用隱式調用方法。示例文件buffer_reuse.cpp給出了一種隱式調用的方法。細節見相應的源代碼文件。

源代碼文件:buffer_resuse.cpp


// buffer_reuse: 重用緩衝區測試示例;

#include <stdio.h>

#include "../../include/x_buffer_manager.h"

#pragma comment(lib, "../../lib/buffmgr.lib")

 

int main(int argc, char* argv[])

{

     IXBufferManage*   pXBM = NULL;

     IXBufferItem*              pXBItem = NULL;

     unsigned long         nBufferSize = 64*1024;

     long                     num = 0;

 

     //創建緩衝管理器;

     CreateBufferManager((void**)&pXBM);

     if(pXBM == NULL)

     {

            printf("Error buffer manager!\r\n");

            return -1;

     }

 

     //初始化緩衝池;

     pXBM->Init(100);

 

     //循環獲取和釋放緩衝區;

     for(num=0; num<20; num++)

     {

            pXBM->GetEmptySample(nBufferSize, &pXBItem);

            if(pXBItem != NULL)

            {

                   //回收緩衝區;

                   pXBItem->Release();

                   pXBItem = NULL;

            }

     }

 

     //釋放緩衝池;

     pXBM->Uninit();

 

     //刪除緩衝管理器;

     pXBM->Release();

     pXBM = NULL;

     return      0;

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章