10

例:

次のスレッドがあると仮定します(この例のスレッドコンテキスト実行メソッドで使用されているものを考慮しないでください。これは説明のためだけです)。

type
  TSampleThread = class(TThread)
  private
    FOnNotify: TNotifyEvent;
  protected
    procedure Execute; override;
  public
    property OnNotify: TNotifyEvent read FOnNotify write FOnNotify;
  end;

implementation

procedure TSampleThread.Execute;
begin
  while not Terminated do
  begin
    if Assigned(FOnNotify) then
      FOnNotify(Self); // <- this method can be called anytime
  end;
end;

OnNotify次に、必要なときにいつでもイベントのメソッドをメインスレッドから変更したいとします。このメインスレッドは、イベントハンドラメソッドを次のメソッドとして実装しThreadNotifyます。

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    FSampleThread: TSampleThread;
    procedure ThreadNotify(Sender: TObject);
  end;

implementation

procedure TForm1.ThreadNotify(Sender: TObject);
begin
  // do something; unimportant for this example
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FSampleThread.OnNotify := nil; // <- can this be changed anytime ?
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  FSampleThread.OnNotify := ThreadNotify; // <- can this be changed anytime ?
end;

質問:

いつでも別のスレッドのコンテキストからワーカースレッドから呼び出すことができるメソッドを変更しても安全ですか?上記の例に示されていることを実行しても安全ですか?

少なくともメソッドポインタは実際にはポインタのペアであり、それを不可分操作と見なすことができるかどうかわからないため、それが絶対に安全かどうかは正確にはわかりません。

4

2 に答える 2

17

いいえ、その操作は決して「アトミック」ではないため、スレッドセーフではありません。はTNotifyEvent2つのポインターで構成されており、これらのポインターが同時に割り当てられることはありません。一方が割り当てられ、次にもう一方が割り当てられます。

割り当て用に生成された32ビットアセンブラはTNotifyEvent、次のような2つの異なるアセンブラ命令で構成されています。

MOV [$00000000], Object
MOV [$00000004], MethodPointer

それが単一のポインタである場合、その操作はアトミックであるため、いくつかのオプションがあります。使用できるオプションは、CPUのメモリモデルの強度によって異なります。

  • CPUが「逐次一貫性」モデルをサポートしている場合、メモリへの書き込み後に発生する読み取りには、保証された新しい値が表示されます。その場合は、値を書き込むだけで済み、メモリバリアやInterlockedメソッドを使用する必要はありません。
  • CPUがストアとロードの並べ替えに関してよりリラックスしている場合は、「メモリバリア」が必要です。その場合、最も簡単な解決策はInterlockedExchangePointerを使用することです

残念ながら、現在のIntelCPUのメモリモデルがどれほど強力かはわかりません。いくつかの再注文が行われる可能性があることを示唆するいくつかの状況証拠があり、それらの使用Interlockedが推奨されますが、どちらかを言うIntelによる決定的な声明を見たことがありません。

証拠:

  • 最近のCPUは「プリフェッチ」を使用しています。これは、あるレベルのロード/ストアの並べ替えを自動的に意味します。
  • SSEは、CPUキャッシュを処理するための特定の命令を導入しました。
于 2013-02-15T14:26:52.880 に答える
2

レジスタサイズ以外に、2つの操作が含まれます。チェックして後で実行します。最小化するには、ローカル変数を作成して使用します。しかしとにかく、これはまだ100%スレッドセーフではありません

var
  LNotify: TNotifyEvent;
begin
  ...
  LNotify := FOnNotify;
  if Assigned(LNotify) then
    LNotify(Self);
end;
于 2013-02-15T17:10:49.787 に答える