一个简易无锁池
一个简易 无锁池
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.