带有 Delphi 字符串的 InterlockedCompareExchangePointer

InterlockedCompareExchangePointer with Delphi string

在我的服务器代码中,我使用以下模式创建“及时”对象:

function TSomeObject.GetChildObjects: TChildObjects;
var
  ChildObjects: TChildObjects;
begin
  if FChildObjects=nil then
    begin
      ChildObjects:=TChildObjects.Create;
// Fill child objects here
      if InterlockedCompareExchangePointer(Pointer(FChildObjects),ChildObjects,nil) <> nil then
        ChildObjects.Free;
    end;
  result:=FChildObjects;
end;

这很好用,但我如何用 Delphi 字符串做类似的事情?例如如果我想在多线程环境中“及时”初始化一个字符串?还是我必须使用关键部分?例如:

function TSomeObject.GetSomeString: string;
var
  s :string;
begin
  if FSomeString='' then
    begin
      s:='Test';
//      InterlockedCompareExchangePointer(Pointer(FSomeString),s,nil);
    end;
  result:=FSomeString;
end;

字符串是引用计数的,所以仅仅交换指针是不够的,你还必须管理引用计数,例如:

function GetStrRec(const S: string): PStrRec; inline;
begin
  Result := PStrRec(PByte(S) - SizeOf(StrRec));
end;

function InterlockedCompareExchangeString(var VTarget: String; const AValue, Compare: String): String; inline;
var
  P: PStrRec;
begin
  Result := '';
  if AValue <> '' then begin
    P := GetStrRec(AValue);
    if P.refCnt > -1 then AtomicIncrement(P.refCnt);
  end;
  Pointer(Result) := InterlockedCompareExchangePointer(Pointer(VTarget), Pointer(AValue), Pointer(Compare));
  if Pointer(Result) <> Pointer(Compare) then begin
    if Result <> '' then begin
      P := GetStrRec(Result);
      if P.refCnt > -1 then AtomicIncrement(P.refCnt);
    end;
    if AValue <> '' then begin
      P := GetStrRec(AValue);
      if P.refCnt > -1 then AtomicDecrement(P.refCnt);
    end;
  end;
end;

或者:

function GetStrRec(const S: string): PStrRec; inline;
begin
  Result := PStrRec(PByte(S) - SizeOf(StrRec));
end;

function InterlockedCompareExchangeString(var VTarget: String; const AValue, Compare: String): String; inline;
var
  P: PStrRec;
begin
  Result := '';
  Pointer(Result) := InterlockedCompareExchangePointer(Pointer(VTarget), Pointer(AValue), Pointer(Compare));
  if Pointer(Result) = Pointer(Comparand) then
  begin
    if AValue <> '' then begin
      P := GetStrRec(AValue);
      if P.refCnt > -1 then AtomicIncrement(P.refCnt);
    end;
  end
  else if Result <> '' then begin
    P := GetStrRec(Result);
    if P.refCnt > -1 then AtomicIncrement(P.refCnt);
  end;
end;

不幸的是,RTL 没有公开函数来操作字符串的引用计数,只能查询它(StringRefCount()), which is why you have to access and manipulate the string's inner StrRec header 手动。