如果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.
答案 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
引用的字符串。