线程安全读取InterlockedIncremented值

时间:2018-02-12 12:04:45

标签: multithreading delphi thread-safety

在我的应用程序中,几个线程递增一些计数器,只有一个读取该值(主线程)。据我所知,如果用双字对齐,读取32位值是线程安全的,所以我使用这样的代码:

{$A8}
TMyStat = class
private
  FCounter: Integer;
public
  procedure IncCounter;
  property Counter: Integer read FCounter;
...

procedure TMyStat.IncCounter;
begin
  InterlockedIncrement(FCounter);
end;

但我不确定混合Interlocked函数和直接访问价值是否安全。

我应该使用InterlockedCompareExchange吗?

function TMyStat.GetCounter: Integer;
begin
  Result := InterlockedCompareExchange(FCounter, 0, 0);
end;

2 个答案:

答案 0 :(得分:3)

您可以使用正常读数。由于FCounter是4对齐的,因此读写保证是原子的。

[这适用于英特尔平台。我真的不知道ARM的行为方式。我猜这个行为是一样的(读取对齐的值是原子的)。]

实际上,如果你只增加一个计数器并阅读它,你甚至不需要InterlockedIncrement。当您读取该值时,您将始终获得预增量或后增量值。你无法将两者混合在一起。

答案 1 :(得分:2)

读取对齐的32位值是 atomic (即保证所有32位保持一致)。但是读取不是同步。您可能无法阅读"当前"值;而是来自缓存的值。

例如:

FCounter := 1; //number of threads running
FResult  := 0; //the answer to everything

然后是你的主题:

procedure ThreadProc;
begin
   FResult := 42; //set the answer before we indicate we're done
   InterlockedDecrement(FCounter);
end;

然后你的代码检查线程是否已完成:

if (FCounter <= 0) then
begin
   //Thread is done; read the answer
   ShowMessage('The answer is: '+IntToStr(FResult));
   Exit;
end;
  

答案是:0

即使您的标志表示线程设置了结果,您也会从本地缓存中读取结果值。

  • 即使FCounterFResult的读取是原子
  • 他们没有同步

如果您使用FCounter,那么您没事。但是,只要您使用其他任何内容并期望它们保持一致,那么您还需要使用某些内容来强制执行同步

对您的问题的严格回答是,读取32位对齐值 已经是原子操作。

但是,完全有可能来到这里阅读这个问题的人可能会忘记阅读是原子的只是你担忧的一小部分。