lock-free这种东西,我只要想想就能感到其复杂程度超出我的想象,但是呢,它又的的确确是好东西,于是就起了收集一套源码的念头(估计多数伸手党也就是这么产生的),还算幸运,找到一个库,简单测试一下,各种lock-free,各种高效。
好吧,打住,今天主打内存池。
写内存池的想法来自CSDN的一位博主,他的一篇文章:一度一写情况下,无锁队列如何实现,直接贴上链接:http://blog.csdn.net/kyee/article/details/4032596
博友dfasri 也对这篇文章所阐述的观点给与了某种意义上的肯定,至少肯定那种算法的正确性。像我这种低端伸手党很自然的就肯定那种无锁队列是可行的了。最主要的还是dfasri在回复中提到了内存池,于是我就想,反正内存池的设计也是可以基于队列的,既然一读一写情况下,队列可以无锁,那么依照此观点,设计一个一线程分配,一线程释放的无锁内存池也差不多可行吧?
仔细想想,如果这种内存池造出来的话,作用虽然不大,但也不是完全没有作用。熟悉IOCP的童鞋都应该知道,工作线程收到数据包后,往往是通过队列将数据交给处理线程的,如果队列是无锁的,内存分配也是无锁的,甚至队列内部的内存分配也是无锁的,那效果应该还可以吧,尤其在一个工作线程对应一个数据处理线程的情况下,效果应该更好吧,我猜的。
嗯,那就试试吧。
免责申明:代码写得非常难看(不是因为写得仓促,完全是由于本人脑袋非常不灵光,加上不思进取,不懂得经常性的自我充电,所以水平实在太烂),不保证代码的安全性,前面已经说了,这份代码全由两位博友引发,本人只是照葫芦画瓢而已,追求也没有下限,看得不爽,请多见谅。
MPool_C语言版:
#include <malloc.h> typedef unsigned long uLong; typedef unsigned long *** HMP1; struct MN1 { uLong uFlag; uLong uResv; MN1 *next; }; struct MP1 { MN1 *pStart; MN1 *pClose; uLong uSize; }; void Mp1_Delete(HMP1 hMp1){} HMP1 Mp1_Create(uLong uSize,uLong uCount) { MP1 *m=(MP1*)::malloc(sizeof(MP1)); m->uSize=uSize+sizeof(MN1); MN1 *n=(MN1*)::malloc(m->uSize); m->pStart=n; for(uLong i=0;i<uCount;i++) { n->uFlag=0; n->uResv=(uLong)m; if(i==uCount-1){n->next=0; break;} n->next=(MN1*)::malloc(m->uSize); n=n->next; }m->pClose=n; return (HMP1)m; } void *Mp1_Malloc(HMP1 hMp1) { MP1 *m=(MP1*)hMp1; uLong u=sizeof(MN1); MN1 *n=m->pStart; MN1 *h=n->next; if(h==0) { n=(MN1*)::malloc(m->uSize); n->uResv=0; return (char*)n+u; }n->uFlag=1; m->pStart=h; return (char*)n+u; } void Mp1_Free(void *pArg) { uLong u=sizeof(MN1); MN1 *n=(MN1*)((uLong)pArg-u); MP1 *m=(MP1*)n->uResv; if(m==0) { ::free((char*)pArg-u); return; //::free(pArg); return; } if(n->uFlag==0) return; n->uFlag=0; n->next=0; m->pClose->next=n; m->pClose=n; }
MPool_C++版本:
//TMPool.h #if !defined(AFX_TMPOOL_H__099C42CF_00CC_495E_944F_7C42E4F0BC44__INCLUDED_) #define AFX_TMPOOL_H__099C42CF_00CC_495E_944F_7C42E4F0BC44__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif struct MPNODE { unsigned long flag; unsigned long resv; MPNODE *next; }; class TMPool { public: static void StaticFree(void *arg); void Free(void *data); void init(unsigned long size,unsigned long count); void* Malloc(); TMPool(); virtual ~TMPool(); private: MPNODE* m_tail; MPNODE* m_head; unsigned long m_size; }; #endif
#include "TMPool.h" #include <malloc.h> TMPool::TMPool(){} TMPool::~TMPool(){} void TMPool::init(unsigned long size,unsigned long count) { m_size=size+sizeof(MPNODE); MPNODE *node=(MPNODE*)::malloc(m_size); m_head=node; for(unsigned long i=0;i<count;i++) { node->flag=0; node->resv=(unsigned long)this; if(i==count-1) { node->next=0; break; } node->next=(MPNODE*)::malloc(m_size); node=node->next; } m_tail=node; } void* TMPool::Malloc() { unsigned long len=sizeof(MPNODE); MPNODE *node=m_head; MPNODE *next=node->next; if(next==0) { node=(MPNODE*)::malloc(m_size); node->resv=0; return (char*)node+len; } node->flag=1; m_head=next; return (char*)node+len; return 0; } void TMPool::Free(void *arg) { MPNODE *node; unsigned long len=sizeof(MPNODE); node=(MPNODE*)((unsigned long)arg-len); if(node->resv==0) { ::free((char*)arg-len); return; } if(node->flag==0) return;//这里很纠结 node->flag=0; node->next=0; m_tail->next=node; m_tail=node; } void TMPool::StaticFree(void *arg) { MPNODE *node; TMPool *Mp; unsigned long len=sizeof(MPNODE); node=(MPNODE*)((unsigned long)arg-len); Mp=(TMPool*)node->resv; if(Mp==0) { ::free((char*)arg-len); return; } if(node->flag==0) return; node->flag=0; node->next=0; Mp->m_tail->next=node; Mp->m_tail=node; }
if(node->flag==0) return;//这里很纠结,这样防止重复释放肯定有问题,改成这样吧,还是觉得不太好
if(0==nterlockedExchange(&node->flag,0)) return;//这样可以防止多线程重复释放,问题是没有必要,因为重复释放本来就是应该杜绝的事情。不这样处理吧,又不甘心,因为重复释放隐患很大,有可能导致将来分配到地址相同的内存。
纠结……
望有能者可以依照此思路实现一个安全可用的内存池,分享一下,以满足伸手党的饥渴,言至此,非常感谢!
经过测试,发现debug下1秒崩溃的问题出在注释掉的那一行,已经修改。
还有,为什么许多代码都写在一行呢?这主要是CSDN这个编辑器使用起来有一定难度,一开始我想尽量让所有代码显示在一个页面内,于是就不断的缩减代码的纵向体积,才造成了这样的结果,虽然后来发现了其他办法,但代码已经被浓缩了,就这样吧。
很抱歉,愚笨是本人的老毛病了。