10

かなり基本的な質問ですが、どこにも尋ねられていません。

次のようなグローバル構造体 (C) があるとします。

struct foo {
  int written_frequently1;
  int read_only;
  int written_frequently2;
};

written_frequentlyこの構造体への割り当てがアトミックであることを 100% 確信できないため、読み取りと書き込みを行うスレッドが多数ある場合は、メンバーにセマフォ (またはその他のロック) が必要であることは明らかです。.

多くのスレッドにread_onlyメンバーを読み取らせ、書き込みを行わせない場合、読み取り専用の構造体アクセスにセマフォが必要ですか?

(直前と直後の場所が常に変更されているという事実がread_onlyメンバーに影響を与えるべきではなく、値を読み取る複数のスレッドが互いに干渉するべきではないため、私はノーと言う傾向があります。しかし、よくわかりません. )


[編集: 私が何を意味しているのかを非常に具体的に明確にするために、この質問をもっとうまく行うべきだったことに今気づきました。当然のことながら、最初に質問したとき、関連する問題のすべてを理解できたわけではありません。もちろん、今質問を包括的に編集すると、これらの優れた回答がすべて台無しになります。私が意味したのは、次のようなものです。

struct bar {
  char written_frequently1[LONGISH_LEN];
  char read_only[LONGISH_LEN];
  char written_frequently2[LONGISH_LEN];
};

私が尋ねた主な問題は、このデータは構造体の一部であるため、他の構造体メンバーの影響を受けているのでしょうか。

メンバーが int であり、したがって書き込みがアトミックである可能性が高いという事実は、この場合、実際には単なるニシンです。]

4

10 に答える 10

7

操作がアトミックであることを保証するには、ミューテックスが必要です。したがって、この特定のケースでは、ミューテックスはまったく必要ない場合があります。 具体的には、各スレッドが1 つの要素書き込み、書き込みがアトミックであり、新しい値が任意の要素 (それ自体を含む) の現在の値から独立している場合、問題はありません。

例:複数のスレッドのそれぞれが、それを更新した最後のスレッドを単純に記録する「last_updated_by」変数を更新します。明らかに、変数自体がアトミックに更新される限り、エラーは発生しません。


ただし、スレッドが一度に複数の要素を読み書きする場合、一貫性を保証するためにミューテックスが必要です。特に、構造全体ではなく要素をロックすることに言及しているためです。

例:スレッドは、構造体の「日」、「月」、および「年」要素を更新します。これは、2 月 31 日のような日付を避けるために、別のスレッドが「月」の増分後、「日」が 1 にラップする前に構造体を読み取らないように、アトミックに発生する必要があります。読み取り時にはミューテックスを尊重する必要があることに注意してください。そうしないと、誤った半分更新された値を読み取る可能性があります。

于 2008-11-05T17:43:25.957 に答える
6

read_only メンバーが実際に読み取り専用である場合、データが変更される危険がないため、同期の必要はありません。これは、スレッドが開始される前に設定されたデータである可能性があります。

頻度に関係なく、書き込み可能なデータの同期が必要になります。

于 2008-11-05T16:30:46.727 に答える
4

変数は初期化時に少なくとも 1 回書き込まれるため、「読み取り専用」は少し誤解を招きます。その場合、最初の書き込みと後続の読み取りが異なるスレッドにある場合は、それらの間にメモリバリアが必要です。そうしないと、初期化されていない値が表示される可能性があります。

于 2008-11-05T17:14:48.183 に答える
3

リーダーにもミューテックスが必要です!

ミューテックスはライター専用であり、リーダーには必要ないという一般的な誤解があるようです。 これは間違っており、この誤解が原因で、診断が非常に困難なバグが発生しています。

その理由を、例の形で説明します。

次のコードで毎秒更新される時計を想像してください。

if (++seconds > 59) {        // Was the time hh:mm:59?
   seconds = 0;              // Wrap seconds..
   if (++minutes > 59)  {    // ..and increment minutes.  Was it hh:59:59?
     minutes = 0;            // Wrap minutes..
     if (++hours > 23)       // ..and increment hours.  Was it 23:59:59?
        hours = 0;           // Wrap hours.
    }
}

hoursコードがミューテックスによって保護されていない場合、更新の進行中に別のスレッドが、minutes、およびseconds変数を読み取ることができます。上記のコードに従います。

【深夜0時前開始】23:59:59
[WRITER は秒を増やします] 23:59:60
[WRITER ラップ秒] 23:59:00
【WRITER刻み分】23:60:00
【WRITER折り返し分】23:00:00
[WRITER 刻み時間] 24:00:00
[WRITER ラップ時間] 00:00:00

最初のインクリメントから6 ステップ後の最終動作までの時間は無効です。リーダーがこの期間中にクロックをチェックすると、値が正しくないだけでなく、違法である可能性があることがわかります。また、コードは時間を直接表示せずに時計に依存する可能性が高いため、追跡が困難なことで有名な「跳弾」エラーの典型的な原因です。

修正は簡単です。

クロック更新コードをミューテックスで囲み、実行中にミューテックスもロックするリーダー関数を作成します。これで、リーダーは更新が完了するまで待機し、ライターは読み取り中に値を変更しません。

于 2008-11-06T04:11:24.973 に答える
2

いいえ。

一般に、リソースへの同時アクセスを防ぐためにセマフォが必要です (intこの場合は an)。ただし、read_onlyメンバーは読み取り専用であるため、アクセス間/アクセス中に変更されません。アトミックな読み取りである必要さえないことに注意してください。何も変わらなければ、常に安全です。

初期設定はどうしていますread_onlyか?

于 2008-11-05T16:35:41.163 に答える
1

実際のロックフリープログラミングに関するこれらの論文のいずれかを読んだり、提供されたスニペットを分析して理解することを楽しむことができます.

于 2009-03-17T12:09:02.603 に答える
1

すべてのスレッドが読み取り専用の場合、セマフォは必要ありません。

于 2008-11-05T16:30:30.427 に答える
0

以前の回答に追加:

  1. この場合、自然な同期パラダイムは、セマフォではなく、相互排除です。
  2. 読み取り専用変数にミューテックスは必要ないことに同意します。
  3. 構造の読み取り/書き込み部分に一貫性制約がある場合、一般に、操作をアトミックに保つために、それらすべてに対して1 つのミューテックスが必要になります。
于 2008-11-05T16:47:32.513 に答える
0

すべての素晴らしい回答者 (およびすべての素晴らしい回答) に感謝します。

総括する:

構造体の読み取り専用メンバーが存在する場合 (この場合、スレッドが読み取りを希望するずっと前に値が一度設定された場合)、このメンバーを読み取るスレッドは、ロック、ミューテックス、セマフォなどを必要としません。他の同時実行保護。

これは、他のメンバーが頻繁に書き込まれる場合でも当てはまります。異なる変数がすべて同じ構造体の一部であるという事実は、違いはありません。

于 2009-10-27T10:59:49.097 に答える
0

関数呼び出しの背後にある各フィールドを非表示にします。書き込み専用フィールドにはセマフォがあります。読み取り専用は値を返すだけです。

于 2008-11-05T16:31:33.777 に答える