DelphiXE10.2.3实现线程安全访问数据和对象(五)——实现原子自旋锁的无锁链表

   之前看过MS的无锁单链表,效率确实非常高,但有点遗憾是Windows的Api函数,不能跨平台使用,而且要求保存的数据必须内存对齐,很不爽,当时看了具体汇编实现后,还是没完全明白过来为什么需要内存对齐,不知道该怎么实现为跨平台的无锁单链表,在之后写无锁Hash时,仔细研究并测试了Delphi原子操作TInterlocked.CompareExchange函数,猛然醒悟。

   此无锁链表可跨平台使用,并且不需要数据的内存对齐,效率与MS的无锁单链表相同,有点爽。

   无锁单链表的应用场景在对保存的数据push和pop无先进先出要求,单纯的实现对象、结构指针等数据的存取,这点在《DelphiXE10.2.3实现线程安全访问数据和对象(四)——实现原子自旋锁的无锁对象池》中得到了应用,实现对象池中记录哪些对象是可以被取出的,经过实际测试,效率确实很高。

{ ********************************************************************************
  组件名称:无锁链表
  组件说明:
  1、多线程安全读写,没有使用线程互斥锁类低效率方法
  2、使用一个单独的空闲链表来实现一个简单的内存池,避免大量的Getmem和Freemem操作
  3、同时,由于使用了链表内存池,就把链表结构封装在内部,使调用者无法操作链表结构,增加可靠性
  4、重要的是,线程读写均衡的情况下,读写效率也奇高
  5、更重要的,跨平台!
  创建者:晴空无彩虹 QQ群:733975324
  版本号:0.1
  创建日期:2018-05-10
  注意事项:
  ******************************************************************************** }

unit uLockFreeLinkedList;
interface
uses System.Classes, System.SyncObjs, System.SysUtils;
type
  // 创建新的单链表和元素事件,调用者要根据需要决定是否实现该自动创建对象过程
  TCreateSingleLinkedList<T> = procedure(var Value: T) of object;
  // 释放单链表和元素事件,调用者必须实现该对象释放过程
  TDestroySingleLinkedList<T> = procedure(var Value: T) of object;
  // 无锁链表
  TLockFreeLinkedList<T> = Class
  private type
    // 单链表
    PSingleLinkedList = ^TSingleLinkedList;
    TSingleLinkedList = record
      Element: T;
      Next: PSingleLinkedList;
    end;
  private
    FLinkedListHead: PSingleLinkedList; // 链表头
    FIdleLinkedListHead: PSingleLinkedList; // 空闲链表
    FValidCount: integer; // 有效链表个数
    FIdleCount: integer; // 空闲链表个数
    FDoDestroy: boolean; // 准备释放对象了,赶快退出通道
    FCreateSingleLinkedList: TCreateSingleLinkedList<T>;
    FDestroySingleLinkedList: TDestroySingleLinkedList<T>;
    function GetValidCount: integer;
    function GetIdleCount: integer;
    // 内部压入操作
    function InternalPush(var Value: T): boolean;
    // 内部弹出操作
    function InternalPop(var Value: T): boolean;
    // 压入空闲链表
    procedure PushIdleLinkedList(var SingleLinkedList: PSingleLinkedList);
    // 弹出空闲链表
    function PopIdleLinkedList: PSingleLinkedList;
  public
    constructor Create();
    destructor Destroy; override;
    function Push(var Value: T): boolean;
    function Pop(var Value: T): boolean;
    // 有效链表个数
    property ValidCount: integer read GetValidCount;
    // 空闲链表个数
    property IdleCount: integer read GetIdleCount;
    // 创建新的单链表和元素事件
    property DoCreateSingleLinkedList: TCreateSingleLinkedList<T>
      read FCreateSingleLinkedList write FCreateSingleLinkedList;
    // 释放单链表和元素事件
    property DoDestroySingleLinkedList: TDestroySingleLinkedList<T>
      read FDestroySingleLinkedList write FDestroySingleLinkedList;
  End;

implementation

constructor TLockFreeLinkedList<T>.Create();
begin
  inherited;
  FValidCount := 0;
  FIdleCount := 0;
  FDoDestroy := false;
  FLinkedListHead := nil;
  FIdleLinkedListHead := nil;
end;


destructor TLockFreeLinkedList<T>.Destroy;
var
  Value: T;
  SingleLinkedList: PSingleLinkedList;
begin
  FDoDestroy := true;
  sleep(10);
  // 先释放所有元素
  while FLinkedListHead <> nil do
  begin
    self.InternalPop(Value);
    // 将单链表中的元素都交给调用者去释放
    if Assigned(DoDestroySingleLinkedList) then
      DoDestroySingleLinkedList(Value);
  end;
  // 再释放所有链表
  while FIdleLinkedListHead <> nil do
  begin
    SingleLinkedList := PopIdleLinkedList();
    freemem(SingleLinkedList);
  end;
  inherited;
end;


function TLockFreeLinkedList<T>.GetValidCount: integer;
begin
  result := TInterlocked.CompareExchange(FValidCount, 0, 0);
end;


function TLockFreeLinkedList<T>.GetIdleCount: integer;
begin
  result := TInterlocked.CompareExchange(FIdleCount, 0, 0);
end;


// 压入空闲链表
procedure TLockFreeLinkedList<T>.PushIdleLinkedList(var SingleLinkedList
  : PSingleLinkedList);
begin
  while true do
  begin
    SingleLinkedList^.Next := FIdleLinkedListHead;
    if TInterlocked.CompareExchange(Pointer(FIdleLinkedListHead),
      SingleLinkedList, SingleLinkedList^.Next) <> SingleLinkedList^.Next then
      continue;
    TInterlocked.Increment(FIdleCount);
    exit;
  end;
end;


// 弹出空闲链表
function TLockFreeLinkedList<T>.PopIdleLinkedList: PSingleLinkedList;
begin
  while true do
  begin
    result := FIdleLinkedListHead;
    if (result = nil) and (not FDoDestroy) then
    begin
      Getmem(result, sizeof(TSingleLinkedList));
      result^.Next:=nil;
      exit;
    end;
    if TInterlocked.CompareExchange(Pointer(FIdleLinkedListHead), result^.Next,
      result) <> result then
      continue;
    TInterlocked.Decrement(FIdleCount);
    exit;
  end;
end;


function TLockFreeLinkedList<T>.Push(var Value: T): boolean;
begin
  result := false;
  // 如果已经在释放对象,就直接退出
  if FDoDestroy then
    exit;
  // 内部压入操作
  result := InternalPush(Value);
end;


function TLockFreeLinkedList<T>.Pop(var Value: T): boolean;
begin
  result := false;
  // 如果已经在释放对象,就直接退出
  if FDoDestroy then
    exit;
  // 内部弹出操作
  result := InternalPop(Value);
end;


// 内部压入操作
function TLockFreeLinkedList<T>.InternalPush(var Value: T): boolean;
var
  SingleLinkedList: PSingleLinkedList;
begin
  result := false;
  // 从空闲链表中取一个出来使用
  SingleLinkedList := PopIdleLinkedList();
  if SingleLinkedList <> nil then
  begin
    SingleLinkedList.Element := Value;
    while true do
    begin
      SingleLinkedList^.Next := FLinkedListHead;
      if TInterlocked.CompareExchange(Pointer(FLinkedListHead),
        SingleLinkedList, SingleLinkedList^.Next) <> SingleLinkedList^.Next then
        continue;
      TInterlocked.Increment(FValidCount);
      result := true;
      exit;
    end;
  end;
end;


// 内部弹出操作
function TLockFreeLinkedList<T>.InternalPop(var Value: T): boolean;
var
  SingleLinkedList: PSingleLinkedList;
begin
  result := false;
  while true do
  begin
    SingleLinkedList := FLinkedListHead;
    if SingleLinkedList = nil then
      if Assigned(DoCreateSingleLinkedList) and not FDoDestroy then
      begin
        DoCreateSingleLinkedList(Value);
        result := true;
        exit;
      end
      else // 如果调用者没有创建单链表事件,就直接返回
        exit;
    if TInterlocked.CompareExchange(Pointer(FLinkedListHead),
      SingleLinkedList^.Next, SingleLinkedList) <> SingleLinkedList then
      continue;
    Value := SingleLinkedList^.Element;
    // 压入空闲链表
    PushIdleLinkedList(SingleLinkedList);
    TInterlocked.Decrement(FValidCount);
    result := true;
    exit;
  end;
end;

end.

     使用方法比较简单,源码备注都基本写了,使用时就通过push和pop存取对象,如果调用者实例化了自动创建对象事件,在链表为空的情况下,会调用该事件,使pop过程一定会取到一个可用对象。

    需要注意的是,单链表pop出对象后,是否push回链表取决与调用者,但如果不再push回去,则调用者自己处理该对象的释放过程。

QQ群:DELPHI开发者群:733975324



猜你喜欢

转载自blog.csdn.net/u011784006/article/details/80499641