Безопасны ли строковые параметры `const`? - PullRequest
10 голосов
/ 02 мая 2011

Этот код

procedure MyThreadTestA(const AStr: string);

Быстрее, чем

procedure MyThreadTestB(AStr: string);

При выполнении одной и той же работы оба проходят указатель.

Однако версия B 'правильно' обновляет контрольный счет AStr и делает копию, если я его изменяю.
Версия A передает только указатель, и только компилятор не позволяет мне изменить AStr.

Версия A небезопасна, если я делаю грязные трюки в Ассемблере или иным образом, чтобы обойти защиту компилятора, это хорошо известно, но ...

Передано AStrпо ссылке как const параметры потокобезопасны?
Что произойдет, если счетчик ссылок AStr в каком-либо другом потоке обнулится и строка будет уничтожена?

Ответы [ 2 ]

16 голосов
/ 02 мая 2011

Нет, такие трюки не поточнобезопасны.Const запрещает добавление-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.
4 голосов
/ 02 мая 2011

Добавить к ответу Барри: Определенно, потокобезопасно, если передаваемая строка получена из локальной переменной внутри области действия вызывающих.

В этом случае эта локальная переменная будет содержать действительную ссылку иЕдинственный способ (предполагая только действительный код на паскале, не возиться в asm) для этой локальной переменной, которая будет изменена, - это если ваш вызов возвращает.

Это также включает все случаи, когда источник строковой переменной является результатомвызов функции (включая доступ к свойству, например, TStrings.Strings []), потому что в этом случае компилятор должен хранить строку в локальной переменной temp.

Проблемы с безопасностью потока могут возникнуть, только если вы передаете напрямуюстрока из места, где эта строка может быть изменена (тем же или другим потоком) до того, как ваш вызов вернется.

...