增加refcounts +移动字符串线程安全吗?

时间:2014-09-03 13:34:20

标签: delphi thread-safety

如果Source数组位于只读集合中,只能在COW(写时复制)操作后更改, 这种克隆字符串数组线程安全的方法吗?

  for i:= Low(Source) to High(Source) do begin
    InterlockedIncStringRefCount(@Source[a]);
  end;
  Move(Source[0], Dest[0], Size * SizeOf(string));

这是一个示例应用程序,用于查看正在运行的克隆 辅助移动的速度是System.CopyArray的两倍 如果我在增量上放下互锁,速度再次翻倍 (但是我需要在复制时锁定阵列,这可能会花费更多)。

program Project10;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  System.Math
  //,FastMove
 ;
const
  Size = 100 * 1000;

function RDTSC: Int64;
asm
  RDTSC;
end;

procedure InterlockedIncStringRefCount(p: pointer);
asm
  mov eax,[eax]
  test eax,eax
  jz @done 
  mov ecx,[eax-8]
  inc ecx
  jz @done //Do not touch constant strings.
  lock inc dword ptr[eax-8];
@done: 
end;

var
  Source: array[0..Size] of string;
  Dest: array of string;
  i,a: Integer;
  StartTime: Int64;
  Duration, MinDuration: Int64;

begin
  for i:= 0 to Size do begin
    Source[i]:= IntToStr(i);
  end;
  SetLength(Dest, Size);
  //Assisted move
  MinDuration:= MaxInt;
  for i:= 0 to 100 do begin
    StartTime:= RDTSC;
    for a:= 0 to Size-1 do begin
      InterlockedIncStringRefCount(@Source[a]);
    end;
    {FastMove.}Move(Source[0], Dest[0], Size * SizeOf(string));
    Duration:= RDTSC - StartTime;
    MinDuration:= Min(Duration, MinDuration);
  end;
  Writeln('Assisted move took '+IntToStr(MinDuration div 1000)+' ticks');
  Readln;
end.

1 个答案:

答案 0 :(得分:2)

附带条件是Source是只读的,此代码是线程安全的。它与 system 单元的字符串赋值实现基本相同,只是您的代码通过Move批量复制引用,而不是单独复制每个字符串引用。

您提到锁定Source的替代方法,然后在没有互锁操作的情况下递增引用计数。那是错的。锁定Source没有做任何事情,因为那不是正在发生变化的事情。这是一个例子:

假设线程1正在调用此数组复制代码,从Dest1生成Source。假设线程2已经通过相同的方式生成Dest2。这意味着Source[0]Dest2[0]都指向相同的单个字符串。现在假设线程2运行Dest2[0] := ''以清除其数组的第一个元素。这将减少我们的字符串的引用计数,而不受线程1的操作的任何保护。如果线程1使用非互锁操作来修改该字符串的引用计数,同时线程2正在修改它,那么您就有竞争条件。该线程1锁定对Source的访问权限是无关紧要的,因为线程2没有对Source起作用。它仅作用于恰好由Source引用的字符串。