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这个编辑器使用起来有一定难度,一开始我想尽量让所有代码显示在一个页面内,于是就不断的缩减代码的纵向体积,才造成了这样的结果,虽然后来发现了其他办法,但代码已经被浓缩了,就这样吧。
很抱歉,愚笨是本人的老毛病了。