锁定多个读者单一作家

dum*_*uch 5 delphi multithreading lock-free

我有一个内存数据结构,由多个线程读取,只由一个线程写入.目前我正在使用一个关键部分来使这个访问线程安全.不幸的是,即使只有另一个读者访问它,它也会阻止读者.

有两种方法可以解决这个问题:

  1. 使用TMultiReadExclusiveWriteSynchronizer
  2. 通过使用无锁方法消除任何阻塞

对于2.到目前为止,我已经得到了以下内容(任何无关紧要的代码都被遗漏了):

type
  TDataManager = class
  private
    FAccessCount: integer;
    FData: TDataClass;
  public
    procedure Read(out _Some: integer; out _Data: double);
    procedure Write(_Some: integer; _Data: double);
  end;

procedure TDataManager.Read(out _Some: integer; out _Data: double);
var
  Data: TDAtaClass;
begin
  InterlockedIncrement(FAccessCount);
  try
    // make sure we get both values from the same TDataClass instance
    Data := FData;
    // read the actual data
    _Some := Data.Some;
    _Data := Data.Data;
  finally
    InterlockedDecrement(FAccessCount);
  end;
end;

procedure TDataManager.Write(_Some: integer; _Data: double);
var
  NewData: TDataClass;
  OldData: TDataClass;
  ReaderCount: integer;
begin
  NewData := TDataClass.Create(_Some, _Data);
  InterlockedIncrement(FAccessCount);
  OldData := TDataClass(InterlockedExchange(integer(FData), integer(NewData));
  // now FData points to the new instance but there might still be
  // readers that got the old one before we exchanged it.
  ReaderCount := InterlockedDecrement(FAccessCount);
  if ReaderCount = 0 then
    // no active readers, so we can safely free the old instance
    FreeAndNil(OldData)
  else begin
    /// here is the problem
  end;
end;
Run Code Online (Sandbox Code Playgroud)

不幸的是,在替换OldData实例后,有一个小问题.如果Read方法中当前没有其他线程(ReaderCount = 0),则可以安全地处理它,就是这样.但如果情况并非如此,我该怎么办?我可以将它存储到下一次调用并将其置于那里,但Windows调度理论上可以让读者线程在Read方法中休眠,并且仍然有对OldData的引用.

如果您发现上述代码存在任何其他问题,请告诉我相关信息.这将在具有多个核心的计算机上运行,​​并且上述方法将非常频繁地调用.

如果这很重要:我正在使用Delphi 2007和内置内存管理器.我知道内存管理器在创建新类时可能会强制执行某些锁定,但我暂时想忽略它.

编辑:从上面可能还不清楚:对于TDataManager对象的完整生命周期,只有一个线程写入数据,而不是几个可能竞争写访问的线程.所以这是MREW的一个特例.

gab*_*abr 6

我不知道可以在Intel86代码上实现的任何无锁(或上面示例中的微锁定)MREW方法.

对于小型(快速到期)锁定,OmniThreadLibrary中的旋转方法可以正常工作:

type
TOmniMREW = record
strict private
  omrewReference: integer;      //Reference.Bit0 is 'writing in progress' flag
public
  procedure EnterReadLock; inline;
  procedure EnterWriteLock; inline;
  procedure ExitReadLock; inline;
  procedure ExitWriteLock; inline;
end; { TOmniMREW }

procedure TOmniMREW.EnterReadLock;
var
  currentReference: integer;
begin
  //Wait on writer to reset write flag so Reference.Bit0 must be 0 than increase Reference
  repeat
    currentReference := omrewReference AND NOT 1;
  until currentReference = InterlockedCompareExchange(omrewReference, currentReference + 2, currentReference);
end; { TOmniMREW.EnterReadLock }

procedure TOmniMREW.EnterWriteLock;
var
  currentReference: integer;
begin
  //Wait on writer to reset write flag so omrewReference.Bit0 must be 0 then set omrewReference.Bit0
  repeat
    currentReference := omrewReference AND NOT 1;
  until currentReference = InterlockedCompareExchange(omrewReference, currentReference + 1, currentReference);
  //Now wait on all readers
  repeat
  until omrewReference = 1;
end; { TOmniMREW.EnterWriteLock }

procedure TOmniMREW.ExitReadLock;
begin
  //Decrease omrewReference
  InterlockedExchangeAdd(omrewReference, -2);
end; { TOmniMREW.ExitReadLock }

procedure TOmniMREW.ExitWriteLock;
begin
  omrewReference := 0;
end; { TOmniMREW.ExitWriteLock }
Run Code Online (Sandbox Code Playgroud)

我刚刚注意到这里可能存在对齐问题 - 代码应检查omrewReference是否为4对齐.将通知作者.