• ベストアンサー

グローバル変数が正常に参照できない?

Windows2000、VC++6.0 でMFCを使ってダイアログベースのEXEを作っています。 以下のようにグローバル変数をループ処理の終了条件に使ってみたのですが、Release版でビルドしたEXEだと意図した動作となりませんでした。 何か原因について心当たりのある方がいればご教授願います。 ------------------------------------------- BOOL bThreadEndFlag; // 外部変数 ●FuncA()関数でスレッドを作成します。 FuncA() {  bThreadEndFlag = FALSE;  CreateThread(..., ThreadFunc, ...);  Sleep(60000);  bThreadEndFlag = TRUE;  ◆     ・     ・ } ●スレッドではフラグがFALSEの間ループします。 ThreadFunc() {  while(bThreadEndFlag == FALSE ) {   X = Y;   ★  } } ------------------------------------------- 上記ではスレッド作成後、60秒後にはフラグがTRUEになり、スレッドが終了することを想定した造りになってます。 しかし、実際にはループ処理が終わらず、スレッドが終了しませんでした。 論理的にはちょっと理解できません。。。 いろいろ試して、以下の場合は正常に動作しました。 (1)コードは全く同じままで、Debug版でビルドした場合はOKでした。 (2)★の部分に「Sleep(0)」を追加した場合はRelease版でもOKでした。 <備考> ◆の位置でフラグの値がTRUEになっていることは確認しました。

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

  • ベストアンサー
noname#4564
noname#4564
回答No.2

> あとは、最適化ですかね?怪しいのは。 > なんかオプションあった気もしますが。 > 最適化について評価したところ、「実行速度」指定のみ > NGで、他の指定ではOKでした。 ウラを取ってませんが、山カンで、volatile ・・・かな・・・?

参考URL:
http://www.google.co.jp/search?hl=ja&ie=UTF-8&oe=UTF-8&c2coff=1&q=C%E8%A8%80%E8%AA%9E+volatile&lr=lang_ja
nabezo-
質問者

お礼

アドバイス、ありがとうございました。 私もいろいろなサイトを探して、volatile に辿り着きました。 恥ずかしながら、今まで volatile を知りませんでした。 早速、「実行速度」の指定で、変数を volatile宣言して試したところOKでした。 勉強になりました。

その他の回答 (3)

回答No.4

volatileキーワードにたどり着かれたということですので、以下の部分に関して... 『ただ、自分としては「最適化」によりプログラムが論理的に動作しないのは解せないです。。。 もし最適化について詳しく、今回の事象が発生する仕組みについて教えていただけると幸いです。』 お使いのコンパイラは、while節(つまり"{"と"}"とに挟まれた処理)の実行によって、bThreadEndFlagが変更されることはないと考えました。(volatileが付いていない変数ですからね。) したがって、最初の1回目の条件判定はおこなうでしょうが、ループの2回目以降の条件判定は不要であると判断しました。 実行速度優先なので、この不要と判断した部分をアセンブラに落とさなかったのです。 出来上がったバイナリを逆アセンブルして、アセンブラを読むことができればこのことを検証できます。 参考まで。

nabezo-
質問者

お礼

回答ありがとうございました。 その後、私も最適化について勉強し、volatile を知らずに使わなかった自分が悪かった、と納得できました。

  • 405
  • ベストアンサー率50% (17/34)
回答No.3

スレッド間で変数を共有するには、微妙なタイミングが問題になると思います。 デバッグ版やリリース版やsleepを入れる等で正常に動作したりしなかったりするのは、 この微妙なタイミングがズレる事によって起こっていると思います。 私は良く、イベントハンドルを利用して回避していますが、 とりあえず↓を参考にしてみてください。 http://www.kumei.ne.jp/c_lang/sdk/sdk_88.htm

参考URL:
http://www.kumei.ne.jp/c_lang/sdk/sdk_88.htm
  • taka_tetsu
  • ベストアンサー率65% (1020/1553)
回答No.1

本当に60秒後にフラグが変わったことを確認できましたか? おそらく、作成したスレッドが制御を移していないためと思われます。 スレッドの制御はOSが行っていますが、このようなソースだと、制御が移らないことがあります。 なので、明示的に別のスレッドに制御を移す ためにSleepを入れることが必要になります。 あとは、最適化ですかね?怪しいのは。 なんかオプションあった気もしますが。

nabezo-
質問者

補足

素早い回答ありがとうございます。 >本当に60秒後にフラグが変わったことを確認できましたか? はい、ログを入れてフラグが 0 から 1 に更新されたことを確認しました。 最適化について評価したところ、「実行速度」指定のみ NGで、他の指定ではOKでした。 アドバイスありがとうございました。 ただ、自分としては「最適化」によりプログラムが論理的に動作しないのは解せないです。。。 もし最適化について詳しく、今回の事象が発生する仕組みについて教えていただけると幸いです。

関連するQ&A

  • スレッドについて

    スレッドについて、解らないのですが、 以下の文があると、1つのスレッドが終了するか終了しないかにかかわず、 次々とスレッドを立ち上げるものなんですか? いろいろな、APIでコントロール出来ると思いますが、 基本的には、次々とスレッドを立ち上げるものなんですか? 教えてください。 WM_CREATE: CreateThread(NULL,0,ThreadFunc,&A,0,&WID); CreateThread(NULL,0,ThreadFunc,&A,0,&WID); CreateThread(NULL,0,ThreadFunc,&A,0,&WID); CreateThread(NULL,0,ThreadFunc,&A,0,&WID); CreateThread(NULL,0,ThreadFunc,&A,0,&WID);

  • マルチスレッドでの画像描画

    マルチスレッドを使ってロード画面を作ろうとしているのですが、 上手く画像が描画更新してくれません。 スレッドの中身は下記の通りです。よろしくお願いします。 HRESULT GameMain::LoadScreen() { // スレッドの生成 static bool onlyonce_createthread = FALSE; if(onlyonce_createthread ==FALSE) { hTh = (HANDLE)_beginthreadex( NULL, // SECURITY_ATTRIBUTES 構造体へのポインタ 0, // 新規スレッドのスタックサイズ &loadthread, // スレッドの実行開始アドレス this, // 新規スレッドに渡される引数リスト 0, // 新規スレッドの初期状態 (unsigned*)&thID ); // スレッドのIDを格納するためのDWORD型変数へのポインタ onlyonce_createthread =TRUE; } // ローディング画面の描画 static bool loopflg = TRUE; while(loopflg) { int threadCondition = CheckThread( hTh ); switch(threadCondition) { case THREAD_RUNNING: if(graphloaded_flg ==TRUE) { EnterCriticalSection( &m_criticalSection ); load_item = (float)(load_item/MAX_LOAD_ITEM); LeaveCriticalSection( &m_criticalSection ); d2d_control->GaugeDraw(0, 0, load_item); Sleep(100); } break; case THREAD_EXIT: loopflg =FALSE; break; case THREAD_ERROR: return E_FAIL; break; } } return S_OK; }

  • スレッドの削除について

    永久ループのあるスレッドがあって、必要なくなったら、永久ループをブレイクさせようと思ってました。このスレッドのクラスのグローバル変数 boolean kaijo; があって、普段は false でブレイクさせたいときだけ、 true にしようと思ってました。このクラスはインスタンス化しているので、 オブジェクト.kaijo=true; という風にして、スレッドをブレイクさせようと思っていましたが、なぜかkaijoはfalseのままです。確かに オブジェクト.kaijo=true; と代入したはずなのですが、、どう思われますか? またスレッドを停止、あるいは破棄するメソッドはありますか?推奨されている範囲内で。

    • ベストアンサー
    • Java
  • _beginthread でメインのキー操作に影響する

    VC++ V6.0 MFC OwnerDrawボタン ダイアローグベースアプリケーション Win98/Win2000 でCreateThread でスレッドを作成していました。 Thread は while で回し、キャプチャ画像を取り込んでます。 しかし、MFCでは CreateThread API はマズイということなので _beginthread 変更しました。 Win98 環境下でCreateThread の時は問題ありませんでした。 しかし、_beginthread に変更したところ、スレッド開始で砂時計でキー操作ができなくなりました。 作ったスレッドは GetThreadPriority は THREAD_PRIORITY_NORMAL と返ってきます。 そこでTHREAD_PRIORITY_BELOW_NORMALに変更したところ、CreateThread で作ったスレッドと同じレベルのキー操作ができるようになりました。 しかし、処理速度が低く困ってます。 while ループでは20msぐらいの仕事なので 30ms で回るよう経過時間を測って Sleep を入れてあります。 よろしくお願いします。

  • boost::threadでのjoinについて

    スレッド用に作成したループ用関数を自前で終了させてからjoinをすると、thread::m_joinableがfalseになりjoinでassertが出ることがあります。 問題の処理を簡単なモデルで説明しますと、 bool g_finish(false); void thread_loop(void) {  while(true)  {   ...   if(g_finish) break;  } } int main(void) {  boost::thread th(thread_loop);  sleep(100000);  g_finish=true;  th.join(); // assert  return 0; } といった感じになります。実際にはスレッド管理クラスにおいて複数のス レッドが管理されています。 冗長ですが、このような処理をするときにjoinのときにm_joinableがfalse になりassertが出るという事態です。 どなたが知識をお持ちの方がおられましたらご教授願えれば幸いです。

  • 変数をループ内で変更しループ外でも参照したい

    変数をループ内で変更しループ外でも参照したい Linuxのシェルを作成している最中にちょっとした壁にぶつかりました。 元々は以下のような感じの処理でした。 (A)------------------ FLAG=false awk "(NR>=2){print}" ${FILE} | while read LINE_STR do if […]; then FLAG=true fi done -------------------- 状況によってFLAGの値を変更し、あとの処理で FLAGの値に応じて異なる処理を行ないます。 で、少し調べたところパイプすると別プロセスになるので云々と あったのでループの前の定義でも中でも「export FLAG」と 書いてみたのですがダメでした。そういうもんじゃないのかと。 元々は、最初の1行は読み飛ばしたいという要望を持っていたので このような記述だったのですが、少し不本意ですが、 読み込むファイルの1行目も処理対象に含めることにした上で 以下のような記述に変更したところ一応動きました。 (B)------------------ while read LINE_STR do FLAG=true done < ${FILE} -------------------- 対処療法として今はこのようなコードにしましたが完全ではありません。 今自分の知識の中で実現可能な方法だと以下のような感じです。 ・フラグファイルを使用する ・1行読み飛ばした一時ファイルを作成しそれを使う ・読み込むファイルの仕様を変更し1行目のヘッダを削除する ・(B)の方法で読み込み、ループ内でカウンタを持ち、最初だけ  continueする どれでも一応実現は可能ですが、エレガントではありません。 そこで質問することにしました。 以下のどちらかもしくはそれ以外で私の希望を実現する方法を 教えてください。よろしくお願いします ・パイプを使用したループでループ内で変更した変数の値を取得する方法  ※(シェルの制約で出来ないのであれば、その旨を知りたいです) ・パイプを使用せず、1行読み飛ばす方法 ※そもそも1行読み飛ばす方法で 「awk "(NR>=2){print}" ${FILE}」 と書いていますが、これは妥当でしょうか? よりよい記述があればあわせて教えてください。 よろしくお願いします。

  • スレッドの終了を知りたい(WindowsAPI)

    CreateThread()で作成したスレッドの終了を知りたい (具体的には、スレッドが終了するまで待機したい)のですが、 うまくいかず困っています。WindowsAPIに関する本やネットで調べた ところ、WaitForSingleObject()が適用できると考え、 以下のようなプログラムを作成したのですが、 元のスレッドがWaitForSingleObject()のところで 止まると同時に、CreateThread()で作成されたThread_1()も 止まってしまいます。アドバイスいただけますでしょうか。 ----プログラム(該当部分)ここから---- DWORD Thread_1(LPVOID param) {  int i;  char buff[128];  /* iが99のときのみ終了してよい */  while(g_iFlg == 1)  {   for(i = 0; i < 100; i++)   {    Sleep(100);    wsprintf(buff, "%d", i);    SetDlgItemText((HWND)param, IDC_STATIC_1, buff);   }  }  ExitThread(0);  return 0; } BOOL CALLBACK Proc_2(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {  switch(uMsg)  {   case WM_INITDIALOG:    g_iFlg = 1;    g_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Thread_1, (LPVOID)hDlg, 0, &g_dwThread);    return TRUE;   case WM_COMMAND:    switch(LOWORD(wParam))    {     case IDC_BUTTON_CANCEL:     case IDCANCEL:      g_iFlg = 0;      WaitForSingleObject(g_hThread, INFINITE);      CloseHandle(g_hThread);      EndDialog(hDlg, 0);      return TRUE;    }  }  return FALSE; } ----プログラム(該当部分)ここまで----

  • win32 スレッドのハンドルついて

    windowsのスレッドについての質問です。 今、手元にある書籍のスレッドを作る節にCreateThread()関数を使ってスレッドを作るサンプルプログラムが載っています。 その中では CloseHandle()関数が一切呼ばれていません。 しかも、そのサンプルではいくつもスレッドを作り、そのスレッドはプログラム中でいくつも終了するようなものです。CloseHandle()は呼び出さなくても問題ないのでしょうか? しかし、ネットで調べてみるとCloseHandle()を呼び出さなくてはいけないということが書かれていました。 http://www.daccho-it.com/program/WinApi/thread.htm ↑ 参考にしたサイト そこでいくつか質問です。 プロセスが終了するときにスレッドのハンドルは自動的に閉じられるという認識で大丈夫でしょうか? CreateThread()、_beginthreadex関数はCloseHandle()を呼び出す必要があるで大丈夫でしょうか? となると、CreateThread()の戻り値は必ず変数に保存しておく必要があるということですか? 書籍のサンプルではCreateThread()の戻り値はどこにも保存していませんでした、保存しない場合ハンドルが作られないってことはさすがにないですよね? よろしくお願いします。

  • マルチスレッド:スレッドの終了を検知する(Ruby)

    2つ以上のスレッドを生成・動作させ、それらが終了した時点で、メインに処理を移すといったことをRubyでしたいと思っています。 (スレッドが1つならば、メインをstopさせておき、スレッドの終了時に例外処理などを使ってrunさせる方法は思いつくのですが。。) 一つ考えたのは・・・ カウンタ変数をつくり、一つのスレッドが終了したらその値を増やす。 メインではwhileループによってその値を常に数えて、全スレッドの終了を把握する。 ただ無駄にwhileループを回してしまいます。。 一般的な効率の良い手段等あるのでしょうか?

  • CreateThreadのエラー

    <プログラム環境> Windows XP VC++6.0 MFC AppWizard(exe) ダイアログベース <質問> 何も実行しないスレッドを作成するようにコーディングしたのですが、 「error C2440: 'type cast' : '' から 'unsigned long (__stdcall *)(void *)' に変換することはできません。」 というエラーが出ました。 これは、何が原因で、解決方法はありますか? <ソース> void CMyDlg::OnButton1() { HANDLE handle; DWORD id; handle = CreateThread(0,0,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,0,&id); } void CMyDlg::ThreadFunc(void) { } <ヘッダ> void ThreadFunc(void); 宜しければご指摘お願い致します。

専門家に質問してみよう