- ベストアンサー
メモリアクセスの競合について
0または0xffが格納されたあるアドレスに対して、 A: 複数のスレッドが非同期に0xffの書き込みをし、 B: 一つのスレッドが繰り返し読み出して非0の判定をし、0の書き込みをした場合、 プログラムの実行上、どのようなリスクがあるでしょうか。 //global_flagの読み出し、書き込みは、funcA, funcBでしか行わない char global_flag = 0; void funcA(void) { if(use_cache()) { global_flag = 0xff; } } //呼ばれる頻度はfuncAの100分の1以下。 // void funcB(void) { if(global_flag != 0) { //この間にfuncAがglobal_flagに0xffを代入しても、 //もう既にfree_cache()を呼ぶことが確定しているので問題はない global_flag = 0; //以下を実行中にfuncAがglobal_flagに0xffを代入しても、 //次回のfuncB実行時にfree_cache()が呼ばれるので問題はない free_cache(); } }
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
少なくとも、コンパイラに思わぬ最適化をされないよう、 global_flagはvolatile宣言すべきと思います。 (参考) http://proger.blog10.fc2.com/blog-entry-20.html あとは、global_flagの意味が「free_cache()実行後に、 use_cache()が一度以上成功した可能性がある」という意味だと仮定し、 use_cache()の内部処理とfree_cache()の内部処理がお互いに排他制御して 同時実行されないようになっていると仮定するなら、これで問題ないと思います。 (ここで排他制御することを前提にしないと、free_cache()実行中にuse_cacheが 呼ばれた場合の話がたぶんうまくいかなくなります。) ただ、もしそうであればglobal_flagは、 排他制御されたuse_cache()、free_cache()の中で 操作すべきもののように思います。
その他の回答 (3)
- uruz
- ベストアンサー率49% (417/840)
No1です私の回答は電気的(ハードウェア)に同時アクセスは起こらないと言うことです。ソフトウェア的にはソフトしだいで対策が必要なケースも当然あります。
お礼
なるほど、そういう意味ですね。読み直してみて納得です。 誤読してしまい、申し訳ありません。 フォローありがとうございます。
- Tacosan
- ベストアンサー率23% (3656/15482)
・global_flag は何のためにあるのですか? ・use_cache, free_cache はどのような処理をするのでしょうか? 処理に際して何か資源を使うのでしょうか? ・最も重要な問題なのですが, そもそもこのコード片で何をしたいのかが分かりません.
お礼
大分単純化した例ですので、このままでは意味を成さないと思います。 今回の質問の意図は、 同一アドレスにCAS命令などのようなアトミック命令を使わずに、 ON/OFFだけの単純なメッセージを送受信した場合に問題が生じるかどうか、 という点です。 自分で考えただけでも、移植性・メモリモデルによる挙動の違い・キャッシュの仕組みによるパフォーマンスの違い、など色々考えられますので、 自分の気付いていない観点がないかどうか、先入観なしにお答えいただければ、と考えて、このような単純化した例で質問させていただきました。
- uruz
- ベストアンサー率49% (417/840)
複数のスレッドであっても実際のメモリアクセスが同時に行われることはありません。複数のスレッドで動作させても見かけ上独立して同時進行しているように見えますがOSのタイムシェアリング機能でスレッド高速で切り替えながら動作しているに過ぎません また、CPUコアが複数存在する場合はメモリコントローラが各コアからのアクセスを調停しますので問題はおこりません。
お礼
もしその通りだとすると、CAS命令のようなものは必要ないことになってしまいます。 マルチコアの場合には、競合の影響を考える必要があるのではないでしょうか。
お礼
丁寧なアドバイスありがとうございます。 実は、特定用途に特化したDBをフルスクラッチで書いていまして、 ・funcAを含むスレッドは、メモリ上のキャッシュを走査してデータを抽出するワーカースレッド群、 ・funcBを含むスレッドは、ディスクやクラスタ上の他マシンから必要とされるデータを読み込むI/Oスレッドです。 ですから、実際のglobal_flagにあたるモノは、キャッシュの処理単位ごとの フラグを並べた共有メモリ上のメモリブロックになります。 ワーカースレッドは、未処理データ群の中で、I/Oスレッドのキャッシュ 読み込み完了フラグが立っているものを探し、完了しているものを走査開始し、 【走査】完了後、該当キャッシュの利用完了フラグを立てる、という動作をします。 【】内がuse_cache()に当たります。 I/Oスレッドは、キャッシュにデータを読み込み完了してから、そのキャッシュ の読み込み完了フラグを立てます。そして、キャッシュの利用完了フラグを 見つけたら、【各ワーカースレッド毎に存在する未処理データ群をスキャンして、 該当キャッシュが含まれていなければ、キャッシュを廃棄します】。 【】内がfree_cache()に当たります。 ここでの「利用完了フラグ」が、コード片で示した「global_flag」です。そして、 「未処理データ群」「キャッシュの読み込み完了フラグ」も同じようなフラグで 実装しようと考えています。 このような仕組みですので、free_cacheとuse_cacheは、ロックやアトミック命令 などの排他制御なしに同一キャッシュを取り扱わない仕組みになっています。 (use_cache同士は同時実行されますが、キャッシュに対しては読み込みしか しませんので競合は起きないはずです) 独立性の高いスレッドが多数回る仕組みなので、極力バスをロックせず、同時 処理できる検索タスクを増やしたいという動機があり、完全にアトミック命令を 排除出来ないか、ということを検討しています。 volatileの件は、基本を確認させていただき、ありがとうございます。 もし、別のリスクについて思い当たる件がございましたら、アドバイスいただけ れば幸いです。