`const`字符串参数(线程)是否安全

时间:2011-05-01 21:38:34

标签: string delphi parameters const

此代码

procedure MyThreadTestA(const AStr: string);

快于

procedure MyThreadTestB(AStr: string);

虽然做同样的工作,但都传递指针。

然而,版本B'正确'更新AStr的参考次数,并在我更改时复制。
版本A只传递指针,只有编译器阻止我更改AStr

如果我在汇编程序中使用脏技巧或以其他方式规避编译器保护,则版本A不安全,这是众所周知的但是......

通过引用传递AStr作为const参数线程安全吗? 如果某个其他线程中的AStr引用计数变为零并且字符串被销毁,会发生什么?

2 个答案:

答案 0 :(得分:16)

不,这样的技巧不是线程安全的。 Const阻止了add-ref,因此另一个线程的更改将以不可预测的方式影响该值。示例程序,尝试更改const

定义中的P
{$apptype console}
uses SysUtils, Classes, SyncObjs;

type
  TObj = class
  public
    S: string;
  end;

  TWorker = class(TThread)
  public
    procedure Execute; override;
  end;

var
  lock: TCriticalSection;
  obj: TObj;

procedure P(const x: string);
// procedure P(x: string);
begin
  Writeln('P(1): x = ', x);
  Writeln('Releasing obj');
  lock.Release;
  Sleep(10); // give worker a chance to run
  Writeln('P(2): x = ', x);
end;

procedure TWorker.Execute;
begin
  // wait until TMonitor is freed up
  Writeln('Worker started...');
  lock.Acquire;
  Writeln('worker fiddling with obj.S');
  obj.S := 'bar';
  TMonitor.Exit(obj);
end;

procedure Go;
begin
  lock := TCriticalSection.Create;
  obj := TObj.Create;
  obj.S := 'foo';
  UniqueString(obj.S);
  lock.Acquire;
  TWorker.Create(False);
  Sleep(10); // give worker a chance to run and block
  P(obj.S);
end;

begin
  Go;
end.

但它不仅限于线程;修改基础变量位置具有类似的效果:

{$apptype console}
uses SysUtils, Classes, SyncObjs;

type
  TObj = class
  public
    S: string;
  end;

var
  obj: TObj;

procedure P(const x: string);
begin
  Writeln('P(1): x = ', x);
  obj.S := 'bar'; 
  Writeln('P(2): x = ', x);
end;

procedure Go;
begin
  obj := TObj.Create;
  obj.S := 'foo';
  UniqueString(obj.S);
  P(obj.S);
end;

begin
  Go;
end.

答案 1 :(得分:4)

添加到Barry的答案:如果传递的字符串来自调用者范围内的局部变量,那么它绝对是线程安全的。

在这种情况下,局部变量将保存一个有效的引用,并且唯一的方法(假设只有有效的pascal代码,在asm中没有摆弄)要改变的局部变量是你的调用返回。

这还包括字符串变量的源是函数调用的结果(包括属性访问,例如TStrings.Strings [])的所有情况,因为在这种情况下编译器必须将字符串存储在本地临时变量中

只有在调用返回之前直接从可以更改该字符串的位置(通过相同或其他线程)传递字符串时,才会导致线程安全问题。