zoukankan      html  css  js  c++  java
  • 多线程下的内存释放问题

    问题由来,

    考虑设计一个内存池类,http://www.ibm.com/developerworks/cn/linux/l-cn-ppp/index6.html?ca=drs-cn

    内存池类代码如下:

    .h文件

     1 #pragma once
     2 
     3 
     4 #include <memory>
     5 #include <iostream>
     6 #include <windows.h>
     7 using namespace std;
     8 
     9 
    10 #define USHORT    unsigned int
    11 #define ULONG      unsigned long 
    12 #define MEMPOOL_ALIGNMENT  4
    13 
    14 #pragma warning( disable : 4291 )
    15 
    16 struct MemoryBlock
    17 {
    18     unsigned short          nSize;
    19     unsigned short          nFree;
    20     unsigned short          nFirst;
    21     //std::shared_ptr<MemoryBlock> pNext;
    22     MemoryBlock*            pNext;
    23     char                    aData[1];
    24 
    25     static void* operator new(size_t, unsigned short nTypes, unsigned short nUnitSize)
    26     {
    27         return ::operator new(sizeof(MemoryBlock) + nTypes * nUnitSize);
    28     }
    29 
    30     static void  operator delete(void *p, size_t)
    31     {
    32         ::operator delete (p);
    33     }
    34 
    35     MemoryBlock (unsigned short nTypes = 1, unsigned short nUnitSize = 0);
    36     ~MemoryBlock() {}
    37 
    38 };
    39 
    40 class MemoryPool
    41 {
    42 private:
    43     //std::shared_ptr<MemoryBlock>  pBlock;
    44     MemoryBlock*            pBlock;
    45     unsigned short          nUnitSize;
    46     unsigned short          nInitCount;
    47     unsigned short          nGrowSize;
    48     CRITICAL_SECTION        allocSection;
    49     CRITICAL_SECTION        freeSection;
    50 
    51 public:
    52     static unsigned char   nMemoryIsDeleteFlag;            //标记MemoryPool内存是否释放
    53 
    54 public:
    55                      MemoryPool( unsigned short nUnitSize,
    56                                  unsigned short nInitCount = 1024,
    57                                  unsigned short nGrowSize = 256 );
    58                     ~MemoryPool();
    59 
    60     void*           Alloc();
    61     void            Free( void* p );
    62 };
    View Code

    .cpp文件

      1 #include "Memory.h"
      2 
      3 
      4 MemoryBlock::MemoryBlock(unsigned short nTypes /* = 1 */, unsigned short nUnitSize /* = 0 */)
      5     : nSize( nTypes * nUnitSize )
      6     , nFree( nTypes - 1 )
      7     , nFirst(1)
      8     , pNext(NULL)
      9 {
     10     char * pData = aData;                  
     11     for (unsigned short i = 1; i < nTypes; i++) 
     12     {
     13         *reinterpret_cast<unsigned short*>(pData) = i; 
     14         pData += nUnitSize;
     15     }
     16 }
     17 
     18 //不允许其他地方修改
     19 unsigned char MemoryPool::nMemoryIsDeleteFlag = 0;
     20 
     21 // UnitSize值不宜设置为过大值
     22 MemoryPool::MemoryPool( unsigned short _nUnitSize, unsigned short _nInitCount , 
     23     unsigned short _nGrowSize  ) 
     24 {
     25     if ( _nUnitSize > 4 )
     26         nUnitSize = (_nUnitSize + (MEMPOOL_ALIGNMENT-1)) & ~(MEMPOOL_ALIGNMENT-1); 
     27     else if ( _nUnitSize <= 2 )
     28         nUnitSize = 2;              
     29     else
     30         nUnitSize = 4;
     31 
     32     nUnitSize = _nUnitSize;
     33     nInitCount = _nInitCount;
     34     nGrowSize = _nGrowSize;
     35     pBlock = NULL;
     36     
     37     InitializeCriticalSection (&allocSection);
     38     InitializeCriticalSection (&freeSection);
     39     
     40 }
     41 
     42 
     43 void * MemoryPool::Alloc()
     44 {
     45     //内存池没有MemoryBlock时,申请一块内存
     46     EnterCriticalSection (&allocSection);
     47     if ( !pBlock )
     48     {
     49         //auto pp = new(nInitCount,nUnitSize)  MemoryBlock(nInitCount,nUnitSize);
     50         pBlock = new(nInitCount,nUnitSize)  MemoryBlock(nInitCount,nUnitSize);
     51         LeaveCriticalSection (&allocSection);
     52         //返回MemoryBlock中的第一个内存块的地址
     53         return (void*)(pBlock->aData+1);
     54     }
     55 
     56     //内存池非空时
     57     MemoryBlock* pMyBlock = pBlock;                           //从链表头部pBlock开始
     58     while (pMyBlock && !pMyBlock->nFree )                           //搜索可用内存快  
     59         pMyBlock = pMyBlock->pNext;  
     60 
     61     // 最后一个MemoryBlock有空闲的内存块时
     62     if ( pMyBlock )            
     63     {
     64         //找到第一个空闲内存块的地址
     65         char* pFree = pMyBlock->aData + (pMyBlock->nFirst * nUnitSize);
     66 
     67         //MemoryBlock构造时,*((USHORT*)pFree)的值为所在的内存块数;
     68         //nFirst记录本MemoryBlock的第一个空闲内存块的计数;
     69         pMyBlock->nFirst = *((USHORT*)pFree);
     70 
     71         pMyBlock->nFree--;    
     72 
     73         LeaveCriticalSection(&allocSection);
     74         return (void*)pFree;
     75     }
     76     //所有的MemoryBlock都占满时
     77     else                   
     78     {
     79         if ( !nGrowSize )
     80             return NULL;
     81 
     82         pMyBlock = new(nInitCount, nUnitSize) MemoryBlock(nInitCount, nUnitSize);
     83         if ( !pMyBlock )
     84             return NULL;
     85 
     86         pMyBlock->pNext = pBlock ;
     87         pBlock = pMyBlock ;
     88 
     89         LeaveCriticalSection(&allocSection);
     90         return (void*)(pMyBlock->aData);
     91     }
     92 
     93     LeaveCriticalSection(&allocSection);
     94     return (void*)pBlock->aData;
     95 }
     96 
     97 //回收,内存返还给MemoryPool,而不是系统
     98 void MemoryPool::Free( void* p )
     99 {
    100     EnterCriticalSection (&freeSection);
    101     USHORT* pfree_us;   
    102     USHORT pfree_index, pHead;
    103     pfree_us = (USHORT *)p;
    104     MemoryBlock* pMyBlock = pBlock;
    105 
    106     if(pMyBlock == NULL)                                                               //pFree不是属于内存池管理的内存
    107     {
    108         LeaveCriticalSection (&freeSection); 
    109         return;
    110     }
    111 
    112     //定位pFree所在的块
    113     while ( ((ULONG)pMyBlock->aData > (ULONG)pfree_us) ||                        
    114         ((ULONG)pfree_us >= ((ULONG)pMyBlock->aData + pMyBlock->nSize*nUnitSize)) )                            
    115     {
    116         pMyBlock = pMyBlock->pNext;
    117     }
    118 
    119     if(pMyBlock == NULL)                                                               //pFree不是属于内存池管理的内存
    120     {
    121         LeaveCriticalSection (&freeSection); 
    122         return;
    123     }
    124 
    125     //回收pFree
    126     pMyBlock->nFree++;                 
    127     pHead = pMyBlock->nFirst;                                                            //第一个可用索引
    128     pfree_index = ( (ULONG)pfree_us-(ULONG)pMyBlock->aData )/nUnitSize;                    //获取pFree的索引号
    129     pMyBlock->nFirst = pfree_index;                                                     //pFree插入到可用块首
    130     *pfree_us = pHead;                                                                  //之前的块首链入
    131 
    132     //判断是否需要将Block内存返回给系统
    133     if ( (pMyBlock->nFree * nUnitSize)  == pMyBlock->nSize )                      
    134     {    
    135         pBlock = pMyBlock->pNext;                                      
    136         delete pMyBlock;
    137     }
    138     LeaveCriticalSection (&freeSection); 
    139 }
    140 
    141 
    142 MemoryPool::~MemoryPool()
    143 {
    144     if ( nMemoryIsDeleteFlag == 0 )
    145     {
    146         
    147         MemoryBlock*   my_block = pBlock;  
    148         MemoryBlock*   next_block = NULL;  
    149         while( my_block && my_block->nFree < nInitCount )  
    150         {  
    151             next_block = my_block->pNext;  
    152             delete my_block;
    153             my_block = NULL;
    154             my_block = next_block; 
    155         }
    156         DeleteCriticalSection (&allocSection);
    157         DeleteCriticalSection (&freeSection);
    158     }
    159     nMemoryIsDeleteFlag = 1;
    160 }
    View Code

    调用程序

     1 #include "Memory.h"
     2 #include <iostream>
     3 #include <cassert>
     4 
     5 using namespace std;
     6 
     7 #define    NCOUNT         24
     8 #define    NUNITSIZE     254
     9 #define    NALLSIZE      nCount * nUnitSize
    10 #define    THREADCOUNT   64
    11 
    12 int GloalInt = 0;
    13 CRITICAL_SECTION section;
    14 char *p_str = NULL;
    15 
    16 DWORD WINAPI Fun ( LPVOID lpThreadParameter )
    17 {
    18     //避免浅拷贝
    19     MemoryPool &pool = *((MemoryPool *)lpThreadParameter);
    20 
    21     EnterCriticalSection (&section);
    22     //第一个线程释放堆内存后,不允许其他线程再访问该堆内存;
    23     //nMemoryIsDeleteFlag为标记位
    24     //if ( MemoryPool::nMemoryIsDeleteFlag == 0)
    25     //{
    26         //MemoryPool::nMemoryIsDeleteFlag = 1;
    27         if (p_str == NULL)
    28             p_str = (char *)pool.Alloc();
    29 
    30         p_str[0] = 'c';
    31         cout <<  '	' << p_str[0] << endl;
    32 
    33         //把p_str指向的空间释放并归还给内存池[注:不是归还给系统]
    34         pool.Free(p_str);
    35         //MemoryPool::nMemoryIsDeleteFlag = 0;
    36     //}
    37     LeaveCriticalSection (&section);
    38 
    39     return 0;
    40 }
    41 
    42 int main()
    43 {
    44     
    45     //创建一个内存池,每个固定内存块允许加载1024个对象(每个对象大小<=254字节).
    46     //每个MemoryBlock的大小 =(MemoryBlock的大小  + 254 * 1024) ;
    47     //向内存池申请NALLSIZE的内存空间,每个单元块的大小是NUNITSIZE;
    48     MemoryPool pool (NCOUNT, NUNITSIZE) ;
    49 
    50     InitializeCriticalSection (&section);
    51 
    52     HANDLE hThreadHandle[THREADCOUNT];
    53 
    54     //p_str是指向一个NUNITSIZE大小的空间.
    55     char *p_str = (char *)pool.Alloc();
    56 
    57     p_str[0] = 'b';
    58 
    59     //MAXIMUM_WAIT_OBJECTS
    60     //创建线程
    61     for(int i=0;i<THREADCOUNT;i++)
    62         hThreadHandle[i] = CreateThread (NULL,0,Fun,&pool,0,NULL);
    63 
    64     WaitForMultipleObjects (THREADCOUNT,hThreadHandle,TRUE,INFINITE);
    65 
    66     for(int i=0;i<THREADCOUNT;i++)
    67     {
    68         if ( hThreadHandle[i] != NULL )
    69         {
    70             CloseHandle (hThreadHandle[i]) ;
    71             hThreadHandle[i] = NULL;
    72         }
    73     }
    74     // do something...
    75 
    76     //把p_str指向的空间释放并归还给内存池[注:不是归还给系统]
    77     //pool.Free(p_str);
    78 
    79 
    80     //MemoryPool对象析构时,才是内存归还给系统
    81 
    82 
    83     return 0;
    84 }
    View Code

    在线程方法中,得到一个主线程传过来的内存池对象。

    问题,

      1、MemoryPool pool = *((MemoryPool *)lpThreadParameter);,出现浅拷贝的问题。

      答:某线程结束时,析构函数中有释放内存的代码,其他线程再释放就会报错。为此,增加了一个nMemoryIsDeleteFlag的标记变量。

      2、Free、析构函数、Alloc有大量申请内存,读取内存,内存写入和释放内存的操作。由此,运行程序会出现线程A释放了内存,线程B接着又访问该内存的问题。【如:http://www.cnblogs.com/Solstice/archive/2010/02/10/dtor_meets_threads.html的问题】。

      答:使用share_ptr智能指针解决。

    share_ptr替代后的智能指针:

    .h文件

     1 #pragma once
     2 
     3 
     4 #include <memory>
     5 #include <iostream>
     6 #include <windows.h>
     7 using namespace std;
     8 
     9 
    10 #define USHORT    unsigned int
    11 #define ULONG      unsigned long 
    12 #define MEMPOOL_ALIGNMENT  4
    13 
    14 #pragma warning( disable : 4291 )
    15 
    16 struct MemoryBlock
    17 {
    18     unsigned short          nSize;
    19     unsigned short          nFree;
    20     unsigned short          nFirst;
    21     std::shared_ptr<MemoryBlock> pNext;
    22    // MemoryBlock*            pNext;
    23     char                    aData[1];
    24 
    25     static void* operator new(size_t, unsigned short nTypes, unsigned short nUnitSize)
    26     {
    27         return ::operator new(sizeof(MemoryBlock) + nTypes * nUnitSize);
    28     }
    29 
    30     MemoryBlock (unsigned short nTypes = 1, unsigned short nUnitSize = 0);
    31     ~MemoryBlock() {}
    32 
    33 };
    34 
    35 class MemoryPool
    36 {
    37 private:
    38     std::shared_ptr<MemoryBlock>  pBlock;
    39     //MemoryBlock*            pBlock;
    40     unsigned short          nUnitSize;
    41     unsigned short          nInitCount;
    42     unsigned short          nGrowSize;
    43     CRITICAL_SECTION        allocSection;
    44     CRITICAL_SECTION        freeSection;
    45 
    46 public:
    47     //static unsigned char   nMemoryIsDeleteFlag;            //标记MemoryPool内存是否释放
    48 
    49 public:
    50                      MemoryPool( unsigned short nUnitSize,
    51                                  unsigned short nInitCount = 1024,
    52                                  unsigned short nGrowSize = 256 );
    53                     ~MemoryPool();
    54 
    55     void*           Alloc();
    56     void            Free( void* p );
    57 };
    View Code

    .cpp文件

      1 #include "Memory.h"
      2 
      3 
      4 MemoryBlock::MemoryBlock(unsigned short nTypes /* = 1 */, unsigned short nUnitSize /* = 0 */)
      5     : nSize( nTypes * nUnitSize )
      6     , nFree( nTypes - 1 )
      7     , nFirst(1)
      8     , pNext(NULL)
      9 {
     10     char * pData = aData;                  
     11     for (unsigned short i = 1; i < nTypes; i++) 
     12     {
     13         *reinterpret_cast<unsigned short*>(pData) = i; 
     14         pData += nUnitSize;
     15     }
     16 }
     17 
     18 //不允许其他地方修改
     19 //unsigned char MemoryPool::nMemoryIsDeleteFlag = 0;
     20 
     21 // UnitSize值不宜设置为过大值
     22 MemoryPool::MemoryPool( unsigned short _nUnitSize, unsigned short _nInitCount , 
     23     unsigned short _nGrowSize  ) 
     24 {
     25     if ( _nUnitSize > 4 )
     26         nUnitSize = (_nUnitSize + (MEMPOOL_ALIGNMENT-1)) & ~(MEMPOOL_ALIGNMENT-1); 
     27     else if ( _nUnitSize <= 2 )
     28         nUnitSize = 2;              
     29     else
     30         nUnitSize = 4;
     31 
     32     nUnitSize = _nUnitSize;
     33     nInitCount = _nInitCount;
     34     nGrowSize = _nGrowSize;
     35     pBlock = NULL;
     36     
     37     InitializeCriticalSection (&allocSection);
     38     InitializeCriticalSection (&freeSection);
     39     
     40 }
     41 
     42 
     43 void * MemoryPool::Alloc()
     44 {
     45     //内存池没有MemoryBlock时,申请一块内存
     46     EnterCriticalSection (&allocSection);
     47     if ( !pBlock )
     48     {
     49         auto pp = new(nInitCount,nUnitSize)  MemoryBlock(nInitCount,nUnitSize);
     50         pBlock.reset(pp);
     51         LeaveCriticalSection (&allocSection);
     52         //返回MemoryBlock中的第一个内存块的地址
     53         return (void*)(pBlock->aData+1);
     54     }
     55 
     56     //内存池非空时
     57     MemoryBlock* pMyBlock = pBlock.get();                           //从链表头部pBlock开始
     58     while (pMyBlock && !pMyBlock->nFree )                           //搜索可用内存快  
     59         pMyBlock = pMyBlock->pNext.get();  
     60 
     61     // 最后一个MemoryBlock有空闲的内存块时
     62     if ( pMyBlock )            
     63     {
     64         //找到第一个空闲内存块的地址
     65         char* pFree = pMyBlock->aData + (pMyBlock->nFirst * nUnitSize);
     66 
     67         //MemoryBlock构造时,*((USHORT*)pFree)的值为所在的内存块数;
     68         //nFirst记录本MemoryBlock的第一个空闲内存块的计数;
     69         pMyBlock->nFirst = *((USHORT*)pFree);
     70 
     71         pMyBlock->nFree--;    
     72 
     73         LeaveCriticalSection(&allocSection);
     74         return (void*)pFree;
     75     }
     76     //所有的MemoryBlock都占满时
     77     else                   
     78     {
     79         if ( !nGrowSize )
     80             return NULL;
     81 
     82         pMyBlock = new(nInitCount, nUnitSize) MemoryBlock(nInitCount, nUnitSize);
     83         if ( !pMyBlock )
     84             return NULL;
     85 
     86         pMyBlock->pNext = pBlock ;
     87         pBlock.reset(pMyBlock) ;
     88 
     89         LeaveCriticalSection(&allocSection);
     90         return (void*)(pMyBlock->aData);
     91     }
     92 
     93     LeaveCriticalSection(&allocSection);
     94     return (void*)pBlock->aData;
     95 }
     96 
     97 //回收,内存返还给MemoryPool,而不是系统
     98 void MemoryPool::Free( void* p )
     99 {
    100     EnterCriticalSection (&freeSection);
    101     USHORT* pfree_us;   
    102     USHORT pfree_index, pHead;
    103     pfree_us = (USHORT *)p;
    104     MemoryBlock* pMyBlock = pBlock.get();
    105 
    106     if(pMyBlock == NULL)                                                               //pFree不是属于内存池管理的内存
    107     {
    108         LeaveCriticalSection (&freeSection); 
    109         return;
    110     }
    111 
    112     //定位pFree所在的块
    113     while ( ((ULONG)pMyBlock->aData > (ULONG)pfree_us) ||                        
    114         ((ULONG)pfree_us >= ((ULONG)pMyBlock->aData + pMyBlock->nSize*nUnitSize)) )                            
    115     {
    116         pMyBlock = pMyBlock->pNext.get();
    117     }
    118 
    119     if(pMyBlock == NULL)                                                               //pFree不是属于内存池管理的内存
    120     {
    121         LeaveCriticalSection (&freeSection); 
    122         return;
    123     }
    124 
    125     //回收pFree
    126     pMyBlock->nFree++;                 
    127     pHead = pMyBlock->nFirst;                                                            //第一个可用索引
    128     pfree_index = ( (ULONG)pfree_us-(ULONG)pMyBlock->aData )/nUnitSize;                    //获取pFree的索引号
    129     pMyBlock->nFirst = pfree_index;                                                     //pFree插入到可用块首
    130     *pfree_us = pHead;                                                                  //之前的块首链入
    131 
    132     //判断是否需要将Block内存返回给系统
    133 //     if ( (pMyBlock->nFree * nUnitSize)  == pMyBlock->nSize )                      
    134 //     {    
    135 //         pBlock = pMyBlock->pNext;                                      
    136 //         delete pMyBlock;
    137 //     }
    138     LeaveCriticalSection (&freeSection); 
    139 }
    140 
    141 
    142 MemoryPool::~MemoryPool()
    143 {
    144 //     if ( nMemoryIsDeleteFlag == 0 )
    145 //     {
    146 //         
    147 //         MemoryBlock*   my_block = pBlock;  
    148 //         MemoryBlock*   next_block = NULL;  
    149 //         while( my_block && my_block->nFree < nInitCount )  
    150 //         {  
    151 //             next_block = my_block->pNext;  
    152 //             delete my_block;
    153 //             my_block = NULL;
    154 //             my_block = next_block; 
    155 //         }
    156 //         DeleteCriticalSection (&allocSection);
    157 //         DeleteCriticalSection (&freeSection);
    158 //     }
    159 //     nMemoryIsDeleteFlag = 1;
    160 }
    View Code

    调用文件无需改变。

    总结:

      单线程下,因为程序是由前到后的执行顺序,所以内存方面的问题调试较为容易,可以使用new/delete管理内存。多线程下,多个线程下的执行顺序是随机的,不容易调试,这样关于内存方面的问题调试较难重现,所以多线程下考虑使用share_ptr智能指针来管理内存。

    智能指针的介绍:Boost程序库完全开发指南.pdf,其第三章有详细介绍。

      

  • 相关阅读:
    Ubuntu各种indicator汇总
    python 命令行参数获取
    python 内置方法的时间复杂度
    SkipList 跳表
    HBase Snapshot功能介绍
    HBase内部操作日志说明
    HBase参数配置及说明
    HBase 在HDFS 上的目录树
    第一章 重构,第一个案例
    shell来start、stop、restart应用程序模板
  • 原文地址:https://www.cnblogs.com/xuxu8511/p/3146761.html
Copyright © 2011-2022 走看看