一個簡易 無鎖池
1.預先開辟2的冪大小容量,可自增,每次翻倍
2.僅提供思路,工程應用可靠性還不確定。
// 無鎖池
// hezihang @cnblogs.com
//20160608 修正可能存在同時Grow的Bug
unit utAtomPool;
interface
Uses
SysUtils,
SyncObjs;
Type
Int32 = Integer;
UInt32 = Cardinal;
TAtomPoolAbstract = Class
private
FWritePtr: Int32;
FReadPtr: Int32;
FHighBound: UInt32;
FData: array of Pointer;
FLock: Int32;
FCheckThread: Integer;
procedure CheckGrow;
Protected
function AllocItemResource: Pointer; virtual; abstract;
procedure FreeItemResource(Item: Pointer); virtual; abstract;
function GetSize: UInt32;
procedure AllocResources;
procedure FreeResources;
procedure Grow;
Public
function Get: Pointer;
procedure Put(Item: Pointer);
Constructor Create(Size: UInt32); Virtual;
Destructor Destroy; Override;
property Size: UInt32 read GetSize;
End;
TAtomPoolMem4K = class(TAtomPoolAbstract)
function AllocItemResource: Pointer; override;
procedure FreeItemResource(Item: Pointer); override;
end;
Implementation
{$IF defined(WIN32) or defined(WIN64)}
uses Windows;
{$ELSE}
{$I InterlockedAPIs.inc}
{$ENDIF}
const
MAXTHREADCOUNT = 1000; // 從池中申請資源最大線程數
// 創建池,大小必須是2的冪,並且必須大於MAXTHREADCOUNT
Constructor TAtomPoolAbstract.Create(Size: UInt32);
var
OK: Boolean;
Begin
Inherited Create;
OK := (Size and (Size - 1) = 0);
OK := OK and (Size > MAXTHREADCOUNT);
if not OK then
raise Exception.Create(Format('池長度必須大於%d並為2的冪', [MAXTHREADCOUNT]));
try
SetLength(FData, Size);
FHighBound := Size - 1;
FWritePtr := FHighBound;
FReadPtr := 0;
except
Raise Exception.Create('池申請內存失敗');
end;
End;
Destructor TAtomPoolAbstract.Destroy;
Begin
FreeResources;
SetLength(FData, 0);
Inherited;
End;
procedure TAtomPoolAbstract.AllocResources;
var
i: UInt32;
begin
for i := 0 to FHighBound do
FData[i] := AllocItemResource;
end;
procedure TAtomPoolAbstract.FreeResources;
var
i: UInt32;
begin
for i := FHighBound downto 0 do
Self.FreeItemResource(FData[i]);
end;
procedure TAtomPoolAbstract.CheckGrow;
begin
if FLock > 0 then
begin
while FLock = 1 do
Sleep(0);
InterlockedIncrement(FCheckThread);
while FLock > 0 do
Sleep(1);
end;
end;
procedure TAtomPoolAbstract.Put(Item: Pointer);
var
N: UInt32;
begin
CheckGrow;
N := InterlockedIncrement(FWritePtr);
FData[N and FHighBound] := Item;
end;
Function TAtomPoolAbstract.Get: Pointer;
var
N, M, K: UInt32;
begin
N := FWritePtr;
M := FReadPtr;
K := (M + MAXTHREADCOUNT) and FHighBound;
if ((N > M) and (N < K)) or ((N < M) and (N > K)) then
begin
Grow
end;
N := InterlockedIncrement(FReadPtr);
Result := FData[M and FHighBound];
end;
function TAtomPoolAbstract.GetSize: UInt32;
begin
Result := FHighBound + 1;
end;
procedure TAtomPoolAbstract.Grow;
var
i, N: Integer;
begin
if InterlockedCompareExchange(FLock, 1, 0)=0 then
begin
N := Length(FData);
SetLength(FData, N + N);
for i := N to High(FData) do
FData[i] := AllocItemResource;
InterlockedIncrement(FLock);
Sleep(MAXTHREADCOUNT * 4);
// while (FCheckThread<MAXTHREADCOUNT) do Sleep(0);
FHighBound := High(FData);
FCheckThread := 0;
InterlockedExchange(FLock, 0);
end
else
CheckGrow;
end;
{ TAtomPoolMem4K }
function TAtomPoolMem4K.AllocItemResource: Pointer;
begin
GetMem(Result, 4096);
end;
procedure TAtomPoolMem4K.FreeItemResource(Item: Pointer);
begin
FreeMem(Item, 4096);
end;
End.