• ベストアンサー

メモリアクセスの競合について

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(); } }

質問者が選んだベストアンサー

  • ベストアンサー
  • Lchan0211
  • ベストアンサー率64% (239/371)
回答No.3

少なくとも、コンパイラに思わぬ最適化をされないよう、 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()の中で 操作すべきもののように思います。

mememomo
質問者

お礼

丁寧なアドバイスありがとうございます。 実は、特定用途に特化したDBをフルスクラッチで書いていまして、 ・funcAを含むスレッドは、メモリ上のキャッシュを走査してデータを抽出するワーカースレッド群、 ・funcBを含むスレッドは、ディスクやクラスタ上の他マシンから必要とされるデータを読み込むI/Oスレッドです。 ですから、実際のglobal_flagにあたるモノは、キャッシュの処理単位ごとの フラグを並べた共有メモリ上のメモリブロックになります。  ワーカースレッドは、未処理データ群の中で、I/Oスレッドのキャッシュ 読み込み完了フラグが立っているものを探し、完了しているものを走査開始し、 【走査】完了後、該当キャッシュの利用完了フラグを立てる、という動作をします。 【】内がuse_cache()に当たります。  I/Oスレッドは、キャッシュにデータを読み込み完了してから、そのキャッシュ の読み込み完了フラグを立てます。そして、キャッシュの利用完了フラグを 見つけたら、【各ワーカースレッド毎に存在する未処理データ群をスキャンして、 該当キャッシュが含まれていなければ、キャッシュを廃棄します】。 【】内がfree_cache()に当たります。 ここでの「利用完了フラグ」が、コード片で示した「global_flag」です。そして、 「未処理データ群」「キャッシュの読み込み完了フラグ」も同じようなフラグで 実装しようと考えています。 このような仕組みですので、free_cacheとuse_cacheは、ロックやアトミック命令 などの排他制御なしに同一キャッシュを取り扱わない仕組みになっています。 (use_cache同士は同時実行されますが、キャッシュに対しては読み込みしか しませんので競合は起きないはずです) 独立性の高いスレッドが多数回る仕組みなので、極力バスをロックせず、同時 処理できる検索タスクを増やしたいという動機があり、完全にアトミック命令を 排除出来ないか、ということを検討しています。 volatileの件は、基本を確認させていただき、ありがとうございます。 もし、別のリスクについて思い当たる件がございましたら、アドバイスいただけ れば幸いです。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (3)

  • uruz
  • ベストアンサー率49% (417/840)
回答No.4

No1です私の回答は電気的(ハードウェア)に同時アクセスは起こらないと言うことです。ソフトウェア的にはソフトしだいで対策が必要なケースも当然あります。

mememomo
質問者

お礼

なるほど、そういう意味ですね。読み直してみて納得です。 誤読してしまい、申し訳ありません。 フォローありがとうございます。

全文を見る
すると、全ての回答が全文表示されます。
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

・global_flag は何のためにあるのですか? ・use_cache, free_cache はどのような処理をするのでしょうか? 処理に際して何か資源を使うのでしょうか? ・最も重要な問題なのですが, そもそもこのコード片で何をしたいのかが分かりません.

mememomo
質問者

お礼

大分単純化した例ですので、このままでは意味を成さないと思います。 今回の質問の意図は、 同一アドレスにCAS命令などのようなアトミック命令を使わずに、 ON/OFFだけの単純なメッセージを送受信した場合に問題が生じるかどうか、 という点です。 自分で考えただけでも、移植性・メモリモデルによる挙動の違い・キャッシュの仕組みによるパフォーマンスの違い、など色々考えられますので、 自分の気付いていない観点がないかどうか、先入観なしにお答えいただければ、と考えて、このような単純化した例で質問させていただきました。

全文を見る
すると、全ての回答が全文表示されます。
  • uruz
  • ベストアンサー率49% (417/840)
回答No.1

複数のスレッドであっても実際のメモリアクセスが同時に行われることはありません。複数のスレッドで動作させても見かけ上独立して同時進行しているように見えますがOSのタイムシェアリング機能でスレッド高速で切り替えながら動作しているに過ぎません また、CPUコアが複数存在する場合はメモリコントローラが各コアからのアクセスを調停しますので問題はおこりません。

mememomo
質問者

お礼

もしその通りだとすると、CAS命令のようなものは必要ないことになってしまいます。 マルチコアの場合には、競合の影響を考える必要があるのではないでしょうか。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • C言語での単体テストの作成について質問です。

    C言語での単体テストの作成方法がわからずに困っています。 以下のようなA.cの中のFuncA関数のテストを作成しています。 /*** A.c ********************/ #include "B.h" int FuncA (int n) { int temp; if (FuncB() == true) temp = n * 2; else temp = n / 2; return temp; } /****************************/ FuncA関数は中でB.c内のFuncB関数を読んでいますが、 これは、B.h、B.cに定義されている関数です。 このFuncB関数は本物のソースを使用せず、スタブを使用してテストを作成しています。 FuncB関数のスタブは以下のように考えています。 /*** BStub.h ********************/ extern bool retVal extern bool FuncB_Stub(void); /******************************/ /*** BStub.c ********************/ #include "BStub.h" bool retVal; bool FuncB_Stub(void) { return retVal; } /******************************/ できる限り、A.cに単体テスト用のコードを埋め込まずにテストを作成したいのですが、 FuncA関数がFuncB関数ではなく、BStubのFuncB_Stub関数を呼び出すようにするには どのようにしたらよろしいでしょうか?

  • C/C++ return文で関数の返却値を返却

    まず、処理がエラーだと-1を返し、正常だと0を返す関数が2つあり、それぞれint funcA()、int funcB()とします。 この時、それらとは別の関数で以下のようにreturn文を書くとどのように値を返しますか? return funcA() || funcB(); 共に0を返す時、どちらかが0を返し他方は-1を返す時、共に-1を返す時についてご教示願います。 またあえてこのように記述するくらいならノーマルに以下の記述の方が良いですか? if (funcA() != 0 || funcB() != 0 ) { return -1; } else { return 0; } (インデントしていないので読みづらかったらすみません)

  • Javaのメモリ・リークについて

    Javaのメモリ・リークについて 以下のコードはスレッド内で、無限ループをしています。 コンストラクタで呼び出し、ずっとこのままにしておいてもフリーズするか否かが知りたいです。 (jconsole.jarというツールがありますが、まだ理解に至っておりません。) ご存知の方がおられましたら教えてください。 よろしくお願いいたします。 import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; public class ActioneventRoopTest extends JFrame implements Runnable, ActionListener { JButton start = new JButton(); JButton change = new JButton("切り替え"); Boolean flag = true; public static void main(String[] args) { new ActioneventRoopTest(); } public ActioneventRoopTest() { change.addActionListener(this); start.addActionListener(this); this.add(change); this.pack(); this.setVisible(true); Thread thread = new Thread(this); thread.start(); } public void actionPerformed(ActionEvent e) { if (e.getSource() == change) { if (flag) { flag = false; } else if (!flag) { flag = true; } } } public void run() { while (true) { while (flag) { System.out.println("リンゴ"); } while (!flag) { System.out.println("ミカン"); } } } }

    • ベストアンサー
    • Java
  • C言語:引数の型だけ異なる関数を一つにまとめる方法

    以下の引数の型だけ異なる関数を、どうにかして一つにまとめたいのですが、 何か良い方法はないでしょうか? void funcA(_MNGR_TAG mngr){ 処理X; } void funcB(_COPY_MNGR_TAG mngr){ 処理X; } 補足: ・_NODE_TAGと_COPY_NODE_TAGは構造体です。ただし中身は異なります。 ・(引数名が同じため)funcAとfuncBで行っている処理は全く同じです。 注意事項: 関数テーブルやC++言語を使用する方法は除いて下さい

  • サブルーチン内のサブルーチン定義について

    サブルーチン内で定義したサブルーチンで、思い通りにならない挙動で困っています。 'test'を10万回繰り返す文字列の生成を行い、その文字列長を表示する関数を funcA とします。その生成過程では、自分の関数内で宣言した再帰関数 funcB を呼び出します。 #! /usr/local/bin/perl use strict; my $time0; for(my $i=0; $i<10; $i++){   $time0 = times();   &funcA();   print((times() - $time0). "\n"); # funcAに掛かった時間 } sub funcA {   my $buffer = '';   &funcB(1);   print length($buffer) . " : "; # $buffer の文字列長      sub funcB{     my $n = shift;     $buffer .= 'test';     return if($n==100000);     funcB($n+1);   } } この結果が、 400000 :3.063 0 :0.468 0 :0.594 0 :0.766 0 :0.859 0 :1.11 0 :1.187 0 :1.141 0 :1.343 0 :1.469 となり、初回以降 $buffer の長さが0となるのも不可解ですが、funcA の実行時間が増加していくのも理解できません. これを #! /usr/local/bin/perl use strict; my $time0; my $buffer; # 注1 $buffer をファイル内大域変数として宣言 for(my $i=0; $i<10; $i++){   $time0 = times();   &funcA();   print((times() - $time0). "\n"); } sub funcA {   $buffer = ''; # 注2 レキシカル変数宣言をやめた   &funcB(1);   print length($buffer) . " : ";      sub funcB{     my $n = shift;     $buffer .= 'test';     return if($n==100000);     funcB($n+1);   } } とすると、結果は 400004 :3.188 400004 :0.234 [以降、上にほぼ同じ] と文字列長は正しいものの,初回以降のfuncA実行時間が極端に減ります. 内部ではどういうことが起こっているのでしょうか.

    • ベストアンサー
    • Perl
  • クリティカルセクション中にreturnすると?

    宜しくお願いします。 コーディングしていてクリティカルセクションについて、ふと疑問に思ったので質問させてください。 以下のように、クリティカルセクション内に条件Aであればreturnを返す関数(funcA)があり、 # クリティカルセクション内を通れるのは1スレッドのみ void funcA( void ){ EnterCriticalSection(&cs);    if( 条件A) {     return; } LeaveCriticalSection(&cs); } あるスレッドが条件Aを満たして LeaveCriticalSection()の前に関数を抜けた場合、他のスレッドはこのクリティカルセクション内を通ることができるのでしょうか? それともEnterCriticalSection()でずっと待ち続ける? もしくはそもそもビルドできない? 試してみればいいじゃないか。という突っ込みはなしで教えてくださる優しい方、 宜しくお願いいたします。

  • 代入によるメモリ劣化について

    マイコンでのプログラムについて、一点疑問があります。 プログラム内でループしている個所の変数代入についてですが、これでマイコン内部のメモリを劣化させてしまわないのか心配です。 マイコン内部のメモリに書込み回数限度があり、それを食いつぶさないのか心配なのです。 以下のプログラムがその例です。 uint8_t i = 0; int main(void) {  while(1)  {   i = 0xff;  } } この処理は内部メモリにダメージを与えてしまうのでしょうか? たとえば、 uint8_t i = 0; int main(void) {  while(1)  {   if(i != 0xff) i = 0xff;  } } このような書き方にすべきなのでしょうか? できる限りシンプルにプログラムしたいので、このような処理が無駄であれば教えてください。 もしくは、もっと効率的な方法があれば教えてください。 よろしくお願いいたします。

  • C# スレッド終了の監視について

    お世話になります。 C#2005でプログラムを作成しております。 マルチスレッドでの、スレッドの終了の監視のことでご質問させていただきます。 下記のコードを実行すると、問題なく実行されます。 ------------------------------------------------------------------------------------ private int SetDataTreeView() { Thread RcvSetThread = new Thread(new ThreadStart(this.RcvDataSet)); RcvSetThread.Start(); } delegate void RcvDataSetDelegate(); void RcvDataGridSet() { /* フォーム内のDatagidviewへの値の代入 */ } void RcvDataSet() { Invoke(new RcvDataSetDelegate(RcvDataGridSet)); } ------------------------------------------------------------------------------------ このスレッドの終了を監視したく、下記のコードを追加してデバッグしてみましたが、 スレッドが実行されませんでした。 ------------------------------------------------------------------------------------ int isRcvSearchFlg = 0; private int SetDataTreeView() { Thread RcvSetThread = new Thread(new ThreadStart(this.RcvDataSet)); RcvSetThread.Start(); // スレッドが終了するまで待機する for (; ; ) { if (isRcvSearchFlg == 1) { break; } } } delegate void RcvDataSetDelegate(); void RcvDataGridSet() { /* フォーム内のDatagidviewへの値の代入 */ isRcvSearchFlg = 1; } void RcvDataSet() { Invoke(new RcvDataSetDelegate(RcvDataGridSet)); } ------------------------------------------------------------------------------------ また、以下も試してみましたが、結果は同じでした。 ------------------------------------------------------------------------------------ private int SetDataTreeView() { Thread RcvSetThread = new Thread(new ThreadStart(this.RcvDataSet)); RcvSetThread.Start(); // スレッドが終了するまで待機する RcvSetThread.Join(); } delegate void RcvDataSetDelegate(); void RcvDataGridSet() { /* フォーム内のDatagidviewへの値の代入処理 */ } void RcvDataSet() { Invoke(new RcvDataSetDelegate(RcvDataGridSet)); } ------------------------------------------------------------------------------------ スレッドの終了を監視する方法がわからず困っております。 お手数ですが、ご教授いただきたくよろしくお願い申し上げます。

  • スレッドが実行中かどうかの確認

    画面の中にボタンを作成し、そのボタンを押すとスレッドが実行される機能を作成しました。 「一度ボタンを押してスレッドが実行中の場合は処理を実行しない」 という処理を実装したいです。 上記の処理を以下のソースで考えていますが(1)の部分がわかりません。 そもそも(1)のようなことが可能かもわかっていない状況です。 (1)のようなことは可能なのでしょうか? どなたか、知恵を貸していただければと思います。 よろしくお願い致します。 /* ソース */ public class ExThread1 extends Thread{  public void run() {   // 処理  } } public class TEST {  public static void main(String[] args){   ExThread1 thread1 = new ExThread1();   // (1)スレッドが実行中ならstartしない   if(スレッドが実行中ではない){    thread1.start();   }  } }

    • ベストアンサー
    • Java
  • 1bppファイルを8bpp(raw)に変換する。

    環境 Windows7 VS2008 SP1 1bppの画像を8bppに変換しようとしているのですが うまくいきません。自分ではこれで 合っていると思うのですが・・・ 何かアドバイスお願いしますm(__)m int Main1bppTo8bpp(WCHAR *filename,int width,int height) { //width,heightは8bppになったときの幅,高さを指定する。 FILE *fpt; _wfopen_s(&fpt,filename,L"rb"); if(fpt==0x00) { MessageBox(NULL,L"1bppTo8bpp Error",L"1bppTo8bpp Error",MB_OK); return -1; } else { unsigned char *layer,*Output; layer=(unsigned char*)calloc(((width/8)+1)*height*sizeof(unsigned char),sizeof(unsigned char)); Output=(unsigned char*)malloc(width*height); //読み込み fread(&layer[0],sizeof(unsigned char),((width/8)+1)*height,fpt); FILE *fpt_output; _wfopen_s(&fpt_output,L"1bppTo8bpp.raw",L"wb"); int i,j,flag=0x00; for(i=0;i<((width/8)+1)*height;i+=((width/8)+1)) { for(j=0;j<((width/8)+1);j++) { //8bit目 if((layer[i+j]&BIT8)==BIT8) { Output[i+j+flag]=0x00; } else { Output[i+j+flag]=0xff; } //7bit目 if((layer[i+j]&BIT7)==BIT7) { Output[i+j+flag+1]=0x00; } else { Output[i+j+flag+1]=0xff; } //6bit目 if((layer[i+j]&BIT6)==BIT6) { Output[i+j+flag+2]=0x00; } else { Output[i+j+flag+2]=0xff; } //5bit目 if((layer[i+j]&BIT5)==BIT5) { Output[i+j+flag+3]=0x00; } else { Output[i+j+flag+3]=0xff; } //4bit目 if((layer[i+j]&BIT4)==BIT4) { Output[i+j+flag+4]=0x00; } else { Output[i+j+flag+4]=0xff; } //3bit目 if((layer[i+j]&BIT3)==BIT3) { Output[i+j+flag+5]=0x00; } else { Output[i+j+flag+5]=0xff; } //2bit目 if((layer[i+j]&BIT2)==BIT2) { Output[i+j+flag+6]=0x00; } else { Output[i+j+flag+6]=0xff; } //1bit目 if((layer[i+j]&BIT1)==BIT1) { Output[i+j+flag+7]=0x00; } else { Output[i+j+flag+7]=0xff; } flag=flag+8; } flag=0x00; } //最終的な「fwrite」はここでする。 fwrite(&Output[0],sizeof(unsigned char),width*height,fpt_output); free(layer); free(Output); fclose(fpt); fclose(fpt_output); } return 0;

このQ&Aのポイント
  • 毎日ブラックコーヒーを飲んでいるが、長期的な影響はあるのか
  • コーヒーは体に良い・悪い両方の記事がある
  • 質問者はペットボトルコーヒーを飲んでおり、不調はないが健康への影響が気になる
回答を見る