8

少しユーモラスなタイトルで申し訳ありません。その中で「安全」という言葉の 2 つの異なる定義を使用しています (明らかに)。

私はスレッド化にかなり慣れていません (まあ、私はスレッド化を何年も使用してきましたが、非常に単純な形式しか使用していません)。現在、いくつかのアルゴリズムの並列実装を作成するという課題に直面しており、スレッドは同じデータで動作する必要があります。次の初心者の間違いを考えてみましょう。

const
  N = 2;

var
  value: integer = 0;    

function ThreadFunc(Parameter: Pointer): integer;
var
  i: Integer;
begin
  for i := 1 to 10000000 do
    inc(value);
  result := 0;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  threads: array[0..N - 1] of THandle;
  i: Integer;
  dummy: cardinal;
begin

  for i := 0 to N - 1 do
    threads[i] := BeginThread(nil, 0, @ThreadFunc, nil, 0, dummy);

  if WaitForMultipleObjects(N, @threads[0], true, INFINITE) = WAIT_FAILED then
    RaiseLastOSError;

  ShowMessage(IntToStr(value));

end;

初心者は、上記のコードでメッセージが表示されることを期待するかもしれません20000000。確かに、最初valueは に等しく0、次にそれをinc掛け20000000ます。ただし、incプロシージャは「アトミック」ではないため、2 つのスレッドが競合し (読み取り、インクリメント、保存の 3 つのことを行うと思いincます)、多くのincs が事実上「失われます」。上記のコードから得られる典型的な値は10030423.

最も簡単な回避策は、InterlockedIncrement代わりに ofを使用することですInc(このばかげた例でははるかに遅くなりますが、それは重要ではありません)。もう 1 つの回避策はinc、クリティカル セクション内に配置することです (はい、このばかげた例でも非常に遅くなります)。

現在、ほとんどの実際のアルゴリズムでは、競合はそれほど一般的ではありません。実際、それらは非常に珍しいかもしれません。私のアルゴリズムの 1 つはDLA フラクタルを作成し、変数の 1 つはinc吸着粒子の数です。ここでの競合は非常にまれであり、さらに重要なことに、変数の合計が 20000000、20000008、20000319、または 19999496 になるかどうかは気にしません。したがって、コードを肥大化させるだけなので、クリティカル セクションを使用しないInterlockedIncrementように誘惑されます。そして、(わずかに)遅くなり、(私が見る限り)メリットがありません。

しかし、私の質問は次のとおりです。インクリメント変数のわずかに「間違った」値よりも、競合のより深刻な結果が生じる可能性がありますか? たとえば、プログラムがクラッシュする可能性はありますか?

確かに、この質問はばかげているように思えるかもしれません。結局のところ、InterlockedIncrement代わりに使用するコストincはかなり低いため (多くの場合、すべてではありません!)、安全にプレイしないのは (おそらく) ばかげているからです。しかし、これが実際に理論レベルでどのように機能するかを知ることは良いことだとも感じているので、この質問は依然として非常に興味深いと思います.

4

1 に答える 1

10

カウントとしてのみ使用される整数のインクリメントの競合が原因で、プログラムがクラッシュすることはありません。間違っている可能性があるのは、正しい答えが得られないことだけです。明らかに、整数を配列へのインデックスとして使用していた場合、またはおそらくそれがポインターであった場合、問題が発生する可能性があります。

この値を信じられないほど頻繁にインクリメントしない限り、インターロックされたインクリメントがパフォーマンスの違いに気付くほどコストがかかるとは想像しがたいです。

さらに、最も効率的な方法は、各スレッドに独自のプライベート カウントを保持させることです。次に、計算の最後にスレッドを結合するときに、個々のスレッド数をすべて合計します。そうすれば、両方の長所を活かすことができます。インクリメントについての競合はなく、正解です。もちろん、偽の共有に引っかからないように対策を講じる必要があります。

于 2012-01-07T13:19:17.253 に答える