本文提供Delphi一個基於原子操作的無鎖隊列,簡易高效。適用於多線程大吞吐量操作的隊列。
可用於Android系統和32,64位Windows系統。
感謝殲10和qsl提供了修改建議!
有如下問題:
1.必須事先足夠大開辟內存,大到不會出現隊列溢出了。
2.隊列大小必須是2的冪
3.不能壓入空指針
4.本程序還未經過工程應用考驗
unit Iocp.AtomQueue; interface Uses SysUtils, SyncObjs; Type TAtomFIFO = Class Protected FWritePtr: Integer; FReadPtr: Integer; FCount:Integer; FHighBound:Integer; FisEmpty:Integer; FData: array of Pointer; function GetSize:Integer; Public procedure Push(Item: Pointer); function Pop: Pointer; Constructor Create(Size: Integer); Virtual; Destructor Destroy; Override; Procedure Empty; property Size: Integer read GetSize; property UsedCount:Integer read FCount; End; Implementation //創建隊列,大小必須是2的冪,需要開辟足夠大的隊列,防止隊列溢出 Constructor TAtomFIFO.Create(Size: Integer); var i:NativeInt; OK:Boolean; Begin Inherited Create; OK:=(Size and (Size-1)=0); if not OK then raise Exception.Create('FIFO長度必須大於等於256並為2的冪'); try SetLength(FData, Size); FHighBound:=Size-1; except Raise Exception.Create('FIFO申請內存失敗'); end; End; Destructor TAtomFIFO.Destroy; Begin SetLength(FData, 0); Inherited; End; procedure TAtomFIFO.Empty; begin while (TInterlocked.Exchange(FReadPtr, 0)<>0) and (TInterlocked.Exchange(FWritePtr, 0)<>0) and (TInterlocked.Exchange(FCount, 0)<>0) do; end; function TAtomFIFO.GetSize: Integer; begin Result:=FHighBound+1; end; procedure TAtomFIFO.Push(Item:Pointer); var N:Integer; begin if Item=nil then Exit; N:=TInterlocked.Increment(FWritePtr) and FHighBound; FData[N]:=Item; TInterlocked.Increment(FCount); end; Function TAtomFIFO.Pop:Pointer; var N:Integer; begin if TInterlocked.Decrement(FCount)<0 then begin TInterlocked.Increment(FCount); Result:=nil; end else begin N:=TInterlocked.Increment(FReadPtr) and FHighBound; //假設線程A調用了Push,並且正好是第1個push, //執行了N:=TInterlocked.Increment(FWritePtr) and FHighBound, //還沒執行FData[N]:=Item, 被切換到其他線程 //此時假設線程B調用了Push,並且正好是第2個push,並且執行完畢,這樣出現FCount=1,第2個Item不為空,而第一個Item還是nil(線程A還沒執行賦值) //假設線程C執行Pop,由於Count>0(線程B的作用)所以可以執行到這里,但此時FData[N]=nil(線程A還沒執行賦值), //因此線程C要等待線程A完成FData[N]:=Item后,才能取走FData[N] //出現這種情況的概率應該比較小,基本上不會浪費太多CPU while FData[N]=nil do Sleep(1); Result:=FData[N]; FData[N]:=nil; end; end; End.
性能測試:
采用天地弦提供的評估程序,進行了一些修改,分別對使用不同的臨界區的隊列進行對比結果如下:
其中Swith是因隊列讀空,進行線程上下文切換的次數