• 简易高效的Delphi原子队列


    本文提供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是因队列读空,进行线程上下文切换的次数

     
  • 相关阅读:
    intel instruction 指令速查
    WinDbg双机调试配置
    MSDN上关于WinDbg的手册
    build temu error about SDL
    taintCheck的实现
    Vim使用taglist功能
    Windows编写driver
    cabal替代脚本
    怎样理解Functor与Monad
    haskell基本语法
  • 原文地址:https://www.cnblogs.com/hezihang/p/3768667.html
Copyright © 2020-2023  润新知