一个简易无锁池

一个简易 无锁池

1.所有读写无等待,不需要判断条件直接读写(除自动扩充容量时),效率是一般带锁或带条件判断池的两倍以上。

2.预先开辟2的幂大小容量,可自增,每次翻倍

3.仅提供思路,工程应用可靠性还不确定。

// 无锁池
// hezihang @cnblogs.com

// 20160228 增加代引用计数器内存块的池,增加编译指令POOLGROW功能,可打开关闭池的自动翻倍增长功能
// 20160225 修正Grow中FWritePtr没有增长Bug
// 20140609 增加Grow临界区,减少等待时间
// 20140608 修正可能存在同时Grow的Bug

unit Iocp.AtomPool;

interface

{ .$DEFINE POOLGROW }

Uses
  System.SysUtils,
  System.SyncObjs;

Type
  Int32 = Integer;
  UInt32 = Cardinal;

  TAtomPoolAbstract = class
  private
    FWritePtr: Int32;
    FReadPtr: Int32;
    FHighBound: UInt32;
    FData: array of Pointer;
{$IFDEF POOLGROW}
    FCs: TCriticalSection;
    FLock: Int32;
    procedure CheckGrow; inline;
    procedure Grow; inline;
{$ENDIF}
  Protected
    function AllocItemResource: Pointer; virtual; abstract;
    procedure FreeItemResource(Item: Pointer); virtual; abstract;
    function GetCapacity: UInt32;
    procedure FreeResources;
  Public
    procedure AllocResources;
    function Get: Pointer;
    procedure Put(Item: Pointer);
    Constructor Create(Capacity: UInt32); Virtual;
    Destructor Destroy; Override;
    property Capacity: UInt32 read GetCapacity;
  End;

  TAtomPoolMem4K = class(TAtomPoolAbstract)
    function AllocItemResource: Pointer; override;
    procedure FreeItemResource(Item: Pointer); override;
  end;

  // 内存块带引用计数器的池,池容量恒定不能增长
  TAtomMemoryPoolRef = class
  private
    FMemory: PByteArray;
    FWritePtr: Int32;
    FReadPtr: Int32;
    FHighBound: UInt32;
    FMemSize: UInt32;
    FData: array of Pointer;
    FDataRef: array of Int32;
  Protected
    function GetCapacity: UInt32;
    procedure AllocResources;
    procedure FreeResources;
  Public
    function Get: Pointer;
    procedure Put(Item: Pointer);
    function IncRef(Item: Pointer): Int32;
    function DecRef(var Item: Pointer): Int32;
    Constructor Create(Capacity: UInt32; MemSize: UInt32);
    Destructor Destroy; Override;
    property Capacity: UInt32 read GetCapacity;
    property MemSize:UInt32 read FMemSize;
  End;

Implementation

const
  MAXTHREADCOUNT = 1000; // 从池中申请资源最大线程数
  // 创建池,大小必须是2的幂,并且必须大于MAXTHREADCOUNT

Constructor TAtomPoolAbstract.Create(Capacity: UInt32);
var
  OK: Boolean;
Begin
  Inherited Create;
  OK := (Capacity and (Capacity - 1) = 0);
  OK := OK and (Capacity > MAXTHREADCOUNT);

  if not OK then
    raise Exception.Create(Format('池长度必须大于%d并为2的幂', [MAXTHREADCOUNT]));
{$IFDEF POOLGROW}
  FCs := TCriticalSection.Create;
{$ENDIF}
  FHighBound := Capacity - 1;
  FReadPtr := 0;
End;

Destructor TAtomPoolAbstract.Destroy;
Begin
  FreeResources;
  SetLength(FData, 0);
{$IFDEF POOLGROW}
  FCs.Free;
{$ENDIF}
  Inherited;
End;

procedure TAtomPoolAbstract.AllocResources;
var
  i: UInt32;
begin
  try
    SetLength(FData, Capacity);
    for i := 0 to FHighBound do
      FData[i] := AllocItemResource;
  except
    Raise Exception.Create('池申请内存失败');
  end;
end;

procedure TAtomPoolAbstract.FreeResources;
var
  i: UInt32;
begin
  for i := FHighBound downto 0 do
    Self.FreeItemResource(FData[i]);
end;

procedure TAtomPoolAbstract.Put(Item: Pointer);
var
  N: UInt32;
begin
{$IFDEF POOLGROW}
  CheckGrow;
{$ENDIF}
  N := TInterlocked.Increment(FWritePtr);
  FData[N and FHighBound] := Item;
end;

Function TAtomPoolAbstract.Get: Pointer;
var
{$IFDEF POOLGROW}
  N, M, K: UInt32;
{$ELSE}
  N: UInt32;
{$ENDIF}
begin
{$IFDEF POOLGROW}
  N := FWritePtr and FHighBound;
  M := FReadPtr and FHighBound;
  K := (M + MAXTHREADCOUNT) and FHighBound;
  if (N > M) and (N < K) then
  // if ((N > M) and (N < K)) or ((N < M) and (N > K)) then
  begin
    Grow
  end;
{$ENDIF}
  N := TInterlocked.Increment(FReadPtr);
  Result := FData[N and FHighBound];
end;

function TAtomPoolAbstract.GetCapacity: UInt32;
begin
  Result := FHighBound + 1;
end;

{$IFDEF POOLGROW}

procedure TAtomPoolAbstract.CheckGrow;
begin
  if TInterlocked.Add(FLock, 0) > 0 then
  begin
    while FLock = 1 do
      Sleep(0);
    FCs.Enter;
    FCs.Leave;
  end;
end;

procedure TAtomPoolAbstract.Grow;
var
  i, N: Integer;
begin
  if TInterlocked.CompareExchange(FLock, 1, 0) = 0 then // 加锁
  begin
    FCs.Enter;
    TInterlocked.Increment(FLock);
    N := Length(FData);
    SetLength(FData, N + N);
    for i := N to High(FData) do
      FData[i] := AllocItemResource;
    TInterlocked.Increment(FLock);
    FHighBound := High(FData);
    FWritePtr := FHighBound;
    FCs.Leave;
    TInterlocked.Exchange(FLock, 0);
  end
  else
    CheckGrow;
end;
{$ENDIF}
{ TAtomPoolMem4K }

function TAtomPoolMem4K.AllocItemResource: Pointer;
begin
  GetMem(Result, 4096);
end;

procedure TAtomPoolMem4K.FreeItemResource(Item: Pointer);
begin
  FreeMem(Item, 4096);
end;

Constructor TAtomMemoryPoolRef.Create(Capacity: UInt32; MemSize: UInt32);
var
  OK: Boolean;
Begin
  Inherited Create;
  OK := (Capacity and (Capacity - 1) = 0);
  OK := OK and (Capacity > MAXTHREADCOUNT);

  if not OK then
    raise Exception.Create(Format('池长度必须大于%d并为2的幂', [MAXTHREADCOUNT]));
  if FMemSize and $10 <> 0 then
    raise Exception.Create('内存块大小必须是16的倍数');

  FMemSize := MemSize;
  try
    AllocResources;
    FHighBound := Capacity - 1;
    FWritePtr := FHighBound;
    FReadPtr := 0;
  except
    Raise Exception.Create('池申请内存失败');
  end;
End;

function TAtomMemoryPoolRef.DecRef(var Item: Pointer): Int32;
var
  N: Integer;
begin
  N := (NativeUInt(Item) - NativeUInt(FMemory)) div FMemSize;
  if (N>=0) and (N<=FHighBound) then
  begin
    Result := TInterlocked.Decrement(FDataRef[N]);
    if Result = 0 then
    begin
      Put(Item);
      Item := nil;
    end;
  end
  else Result:=-1;
end;

Destructor TAtomMemoryPoolRef.Destroy;
Begin
  FreeResources;
  Inherited;
End;

procedure TAtomMemoryPoolRef.AllocResources;
var
  i: UInt32;
  P: PByteArray;
begin
  SetLength(FData, Capacity);
  SetLength(FDataRef, Capacity);
  FillChar(FDataRef[0], Capacity * Sizeof(FDataRef[0]), 0);
  GetMem(FMemory, Length(FData) * FMemSize); // 一次申请所有内存
  P := FMemory;
  for i := 0 to FHighBound do
  begin
    FData[i] := P;
    Inc(P, FMemSize);
  end;
end;

procedure TAtomMemoryPoolRef.FreeResources;
begin
  FreeMem(FMemory, Length(FData) * FMemSize);
  SetLength(FData, 0);
  SetLength(FDataRef, 0);
end;

procedure TAtomMemoryPoolRef.Put(Item: Pointer);
var
  N: UInt32;
begin
  N := TInterlocked.Increment(FWritePtr);
  FData[N and FHighBound] := Item;
end;

Function TAtomMemoryPoolRef.Get: Pointer;
var
  N: UInt32;
begin
  N := TInterlocked.Increment(FReadPtr);
  Result := FData[N and FHighBound];
end;

function TAtomMemoryPoolRef.GetCapacity: UInt32;
begin
  Result := FHighBound + 1;
end;

function TAtomMemoryPoolRef.IncRef(Item: Pointer): Int32;
var
  N: Integer;
begin
  N := (NativeInt(Item) - NativeInt(FMemory)) div FMemSize;
  if (N>=0) and (N<=FHighBound) then
    Result := TInterlocked.Increment(FDataRef[N])
  else
    Result:=-1;
end;

End.

 

 

posted @ 2014-06-08 18:47  子航  阅读(1065)  评论(2编辑  收藏  举报