初めまして、pav777と申します。
複数のスレッドにて1つのint型変数に、書込み/読込みする際、排他制御は必要なので
しょうか?
スレッド構成は以下の通りです。
スレッドA:書込み専用
スレッドB:読込み専用
スレッドC:読込み専用
スレッドD:読込み専用
複数のスレッドで書きこむ可能性がある場合は、排他する必要はあるかと思いますが、
書き込むスレッドが1つの場合、排他する必要があるのかわからず質問しました。
ご回答よろしくお願い致します。
> 書き込むスレッドが1つの場合、排他する必要があるのか
CPUが一気に全てを書き込める/読み込めるか否かが問題です。
一連の書き込み動作中に読み込みが割り込む/読み込み動作中に書き込みが割り込むと
データの一貫性が崩れます。
一般的から言えば、問題ないです。
しかし、各読み込むスレッドにとって、読み込むシーケンスをよく確保するため、排他制
御が必要だと思います。
排他しないで、予定通り処理シーケンスを従っていない場合も発見したことがあります。
読んでる最中に他の読み込みスレッドが割り込むのを心配してるのかな?
そいつは別に問題ないですが。
# readers-writer-lock てーやつやね。
ボタンのイベントやタイマーやエディットボックス等のイベントによりスレッドが
実行するようになっていて、スレッド同士ぶつからない保障があるのなら排他制御は
いりません。
サーバ等のソフトのようにみんな同じタイミングで動く可能性のある場合は排他制御
が必要です。
ん?
書込み専用スレッドを常にイベント待ち状態にして、
読込み専用スレッドが動いてないときにイベントがONになるようにするのが
いいかと思います。
又は、書き込み終了フラグを各スレッド毎に設けてフラグがONのときは読込み
直すとかですね。
僕は後者かな。
επιστημηさんと同様の回答になってしまいますが
書込みと読込みが同時に起こってはいけないので基本的に排他は必要です。
ただ32ビットプロセッサで32ビット変数1つだけにアクセスする場合や
64ビットプロセッサで64ビット変数1つだけにアクセスする場合など
特定の条件下ではソフト側で同期を取らなくても、
物理メモリ操作のレベルで結果的に排他になっているというだけです。
32ビットプロセッサで64ビット変数にアクセスする場合には
上位32ビットと下位32ビットに操作が分かれるので一貫性は保証されません。
私の方は現実的観点から回答したいと思います。
皆さんのおっしゃるとおり、1つのint型変数限定なら、結果として問題ないでしょう。
問題はそこです。 ~~~~~~~~~~
仮に、1つのint型変数というのが、データベースの主キーのようなものだとします。
具体例として、社員番号を想定します。
最初の設計では、社員番号は全社を通してユニークな連番でした。
ところが、ある日、分社化を進めるにあたり、従来の社員番号を改め、会社毎に社員番
号を割り振る形に変更になりました。
結果として、データベースの主キーは[会社ID + 社員番号]になります。
もし、結果として問題ないことだけを利用して、排他処理を省略しており、
そのことを知らない後継者が、この仕様変更を担当することになったらどうでしょう?
int nStaffNum; // 社員番号
↓
struct TStaffNum
{
int nCompanyId; // 会社ID
int nNumber; // 社員番号
};
もし、書き込みスレッドが会社IDを書き込み、社員番号をまだ書き込んでいない状態で
読み込みスレッドが読み込みに来たら、赤の他人と誤認識する(もしくは該当者なし)と
なるわけです。
ちょっと前にはやった[マーフィーの法則]によれば、こういうのは危惧じゃなく、
近い将来、必ず起きるものとして、あらかじめ対策しておくべきと私は考えます。
皆さん、ありがとうございます。
書き込む際に情報が少なく申し訳ありませんでした。
今回のシステムでは、「スレッドA:書込み専用」の処理速度も求められており、各スレ
ッドで共有するデータをなるべく減らすよう設計したのですが、
上記int型変数1つはどうしても共有する必要がありました。
CPUの動作として、int型変数(4バイトの場合)に値を代入した場合、途中まで(例え
ば1バイトまで)書きこんでいる最中に、別スレッドがその変数を読み込むようなこと
が起きるのか、わからず質問をしました。
また、いつもであれば複数スレッドのデータ共有時は、必ず排他制御をかけているので
すが、読込スレッドの処理内容や数により、書込みスレッドの速度が低下する可能性が
あるため、なるべく不必要な処理は実装したくないと言う思惑もありました。
うーん、
書込むための変数を二つ用意して、データ保存用と表示用に分ける。
読込み専用スレッドは、表示用を使う。
そうすればうまくいくと思います。
心配なら、書き込み時にフラグを立てて読み込めないようにすればいいと思います。
または、書き込み終了時にイベントをONにする。
読込み専用スレッドはそのイベント待ちにするとか、まだまだやりようがあると
思います。
スレッドで処理しているのであれば同一プロセス内の排他ですし、
クリティカルセクションでやればそこまで負担も無いのではないかと
思うのですけれど、それすら問題になるくらいシビアな話なんでしょうか。
グローバル領域に置いておいて、クリティカルセクションで排他なら
そこまで足は引っ張らないのではと思うのですけれど。
書き込まれている内容からだけでは厳しい性能を要求されているのだろう
程度しか伝わってこないので何ともいえませんけれど。
全部ひっくりかえしちゃうようだけど
最近はあまり見なくなったマルチ CPU マシン(マルチコア CPU ではなく)で、
その共有メモリの値が両 CPU のL1/L2キャッシュに別々に乗っているような場合に
単純に mov 1命令で排他なしにうまく動くんだったっけ?
そのための Interlocked**** だったような気がするんだが。
まあ Readers-Writer-Lock つまり1書き込みN読み込みだったら問題ないか。
Writer 側 CPU で書いた値が Reader 側 CPU に届くまでに遅延がかかるだけだし。
(メインメモリに Cache Write-Back されるまで届かない)
ハードウエアに期待してしまうのであれば、
期待に沿えるハードウエア上で無いと動作保証は出来ないと言う事になるかと。
この辺の動作保証を何処まで厳密にやるの?と言う話かな。
個人的には性能的な問題がないなら排他を掛けてソフト側でちゃんと制御する方が
安全だと思うし、ハード側の事情に左右されない方が良い気はします。
但し、今のマシンでちゃんと動くなら特に問題なしと考えるならそれも道かと。
そこまでしか動作保障はしませんで済むならと言う話になりますけれど。
ここの所の判断は、それぞれの事情とかもあると思うので一概にこれと言う話は
出来ないと思います。
どこまで保証するのかはそれぞれの会社の考え方もあるでしょうし。
>tetrapodさん
なるほど。そのような環境での動作は別に考慮がいりそうですね。
.NET Framework の Thread.MemoryBarrier メソッドの解説にもそれっぽいことが書かれ
てます。
>MemoryBarrier は、複数の Intel Itanium プロセッサを使用しているシステムなど、
>ウィーク メモリ オーダリングを採用したマルチプロセッサ システムでだけ使用します。
MSのサイトにもロックなし時の考慮点が書かれたページがありましたのでご参考まで。
http://msdn.microsoft.com/ja-jp/library/bb310595(VS.85).aspx
うーーん、
PATIOさんの意見どおり、クリティカルセクションで十分ですね。
「そこまでしなくても」って思っていたのですが、処置が確実ですね。
ハードウエア依存の話もありますが、突き詰めていくとデバイスドライバー
を作らないといけないところまで行きついてしまいます。
PATIOさんの意見にもあるとおり、ひとまずクリティカルセクションまでで
考えておいた方がいいです。