• 締切済み

マルチスレッドでバグが発生します

実際のプログラムでバグが出たので、簡易化したプログラムでテストしてみましたがどこが悪いのか分かりませんでした。 #include <WinSock2.h> #include <vector> #include <process.h> #include <algorithm> #include <stdio.h> using namespace std; CRITICAL_SECTION cs; unsigned __stdcall Login( void * ); int main() {     vector<unsigned int> thID;     vector<HANDLE> hTh;     for( int i=0 ; i<10 ; i++ )     {         printf( "メインスレッド:%d\n", i );         if( (i%2) == 0 )         {             thID.push_back( i );             hTh.push_back( (HANDLE)_beginthreadex(NULL, 0, Login, &i, 0, &thID[i]) );         }     }     while(1)     {         for( int i=0 ; i<10 ; i++ )         {             if( hTh[i] != NULL )             {                 CloseHandle(hTh[i]);             }         }         char a[2];         scanf( "%c", a );         if( strcmp( a, "X" ) == 0 )         {             return 0;         }     } } unsigned __stdcall Login( void *Num ) { int *a = (int *)Num; for( int i=0 ; i<10 ; i++ ) { printf( "サブスレッド%d:%d\n", *a, i ); } return 0; } 出るエラーは以下のようになります Windows によって Server.exe でブレークポイントが発生しました。 ヒープが壊れていることが原因として考えられます。Server.exe または読み込まれた DLL にバグがあります。 あるいは、Server.exe がフォーカスを持っているときに、ユーザーが F12 キーを押したことが原因として考えられます。 可能であれば、出力ウィンドウに詳細な診断情報が表示されます。 if( (i%2) == 0 ) をコメントアウトするとエラーは出ません また、共に 表示される例として(見やすいように空行あり) 以下のように、 スレッド番号が+1されていたり(A) 各スレッド内のループ回数が直前のスレッドの回数を引き継いでいたり(B) スレッドのループ回数が初期化されていたり(C) 呼び出されてないはずのスレッドが起動していたり(D) します メインスレッド:0 メインスレッド:1 サブスレッド1:0 ・・・・・・A(本来はサブスレッド0のはず) サブスレッド1:1 メインスレッド:2 サブスレッド2:3 ・・・・・・B(本来はサブスレッド2:0のはず) サブスレッド2:4 サブスレッド2:5 サブスレッド1:0 ・・・・・・C(本来は1:3のはず) サブスレッド1:1 サブスレッド2:0 サブスレッド3:0 ・・・・・・D(本来はまだ起動してないはず) サブスレッド3:1 メインスレッド:3 どこを直せばいいか教えてください

みんなの回答

回答No.4

> では、どのようにしたらいいのでしょうか? たとえばこんな: #include <WinSock2.h> #include <vector> #include <tuple> #include <process.h> #include <algorithm> #include <stdio.h> using namespace std; CRITICAL_SECTION cs; unsigned __stdcall Login( void * ); int main() { typedef tuple<unsigned int, HANDLE, int> record; vector<record> records; for( int i=0 ; i<10 ; i++ ) { printf( "メインスレッド:%d\n", i ); if ( (i%2) == 0 ) { records.push_back(record()); record& r = records.back(); get<2>(r) = i; get<1>(r) = (HANDLE)_beginthreadex(NULL, 0, Login, &get<2>(r), 0, &get<0>(r) ); } } ...

Null0lluN
質問者

お礼

わざわざありがとうございます。 tupleという物があるんですね。 知らなかったです 調べてみましたが、どういうものかは分かりましたがプログラムで何をしているのか分からなかったのでそのままコピペしてみました 結果 メインスレッド:0~9 サブスレッド-17891602:0~9 サブスレッド0:0~9 サブスレッド-17891602:0~9 サブスレッド-17891602:0~9 サブスレッド8:0~9 となりました 大体道筋が見えてきたように思います。

  • zwi
  • ベストアンサー率56% (730/1282)
回答No.3

今現在の問題のあるコードと実行結果を貼って下さい。

Null0lluN
質問者

補足

あ、申し訳ありません 前略 {     vector<unsigned int> thID;     vector<HANDLE> hTh;     DWORD dwExCode;     for( int i=0 ; i<10 ; i++ )     {         printf( "\nメインスレッド:%d\n", i );         if( (i%2) == 0 )         {             thID.push_back( i );             hTh.push_back( (HANDLE)_beginthreadex(NULL, 0, Login, &i, 0, &thID[i/2]) );         }     }     while(1)     {         for( int i=0 ; i<hTh.size() ; i++ )         {             GetExitCodeThread(hTh[i], &dwExCode);             if( dwExCode != STILL_ACTIVE )             {                 CloseHandle( hTh[i] );                 vector<HANDLE>::iterator end_it = remove( hTh.begin(), hTh.end(), HANDLE(hTh[i]) );                 hTh.erase( end_it, hTh.end() );                 vector<unsigned int>::iterator End_it = remove( thID.begin(), thID.end(), unsigned int(thID[i]) );                 thID.erase( End_it, thID.end() );             }         }         char a[2];         scanf( "%c", a );         if( strcmp( a, "X" ) == 0 )         {             return 0;         }     } } 以下略 結果の一例 M:0 M:1 M:2 S2:0 S3:1~9 M:3 M:4 S3:0 S4:1 S5:2~9 M:5 M:6 S5:0 S7:1 M:7 M:8 S7:2 S9:3~9

  • zwi
  • ベストアンサー率56% (730/1282)
回答No.2

怪しい所です。 1.スレッドに変数寿命が切れたローカル変数iを渡している。 2.vectorでpush_backした要素数よりも多い番号を参照。 (i%2) == 0 )で2回に一回しかpush_backしないのに&thID[i]でi番目を参照しています。 3.以下の部分は10個要素がある前提に成っているが10個無いのでエラーです。  for( int i=0 ; i<10 ; i++ )         {             if( hTh[i] != NULL )             {                 CloseHandle(hTh[i]);             }         } 4.スレッドの終了を待たずにCloseHandleしている。

Null0lluN
質問者

お礼

1.はepistemeさんと同じことですよね? 2.3.あ、本当ですね。だから偶数のときにスレッドを立てるようにしたらエラーになったんですね 4.そこは盲点でした。 とりあえず、2.3.4.を直してこようと思います

Null0lluN
質問者

補足

2.3.4.を直しました 各スレッド内のループ回数が直前のスレッドのループ回数を引き継いでいるのは直りませんでした

回答No.1

 for( int i=0 ; i<10 ; i++ ) {   ...   hTh.push_back( (HANDLE)_beginthreadex(NULL, 0, Login, &i, 0, &thID[i]) ); コロコロ変化している i のポインタを スレッドに引き渡しているのはおかしくない? スレッドに火が付いた時点で i が変化してるかもですよ?

Null0lluN
質問者

お礼

なるほど。確かにそうですね では、どのようにしたらいいのでしょうか?

関連するQ&A

  • マルチスレッドプログラミングについて

    こんばんわ。 マルチスレッドプログラミングを行なっています。開発環境はVC++.NET2003でC言語を用いてコンソールアプリケーションを勉強中です。 以下にプログラムを示します。 以下のプログラムは、 (1)ThAとThBが交互に1~10までカウントUP (2)ThBだけが11~20までカウントUP (3)ThAはTh11から、ThBは21からカウントUP というプログラムを記述したつもりなのですが・・・実行開始後は(1)から(3)のように表示できるのですが、それ以降はThAとThBが交互に表示されてしまいます。 (1)から(3)を繰り返す記述の仕方はありますでしょうか? よろしくお願い致します。 HANDLE hEvent[3]; unsigned __stdcall ThB(void *lpx){ int j; int l=1; for(j=1;j<100;j++){ Sleep(100); printf("ThB%d\n",j); if(l%20==0){ SetEvent(hEvent[0]); } l++; } return 0; } unsigned __stdcall ThA(void *lpx){ int k; int j=1; for(k=1;k<100;k++){ Sleep(100); printf("ThA%d\n",k); if(j%10==0){ WaitForSingleObject(hEvent[0],INFINITE); } j++; } SetEvent(hEvent[2]); return 0; } int main(int argc ,char *argv[]){ unsigned int thID[2]; HANDLE hTh[2]; int i; hEvent[0] = CreateEvent(NULL, TRUE, FALSE, "CH0"); hEvent[1] = CreateEvent(NULL, TRUE, FALSE, "CH1"); hEvent[2] = CreateEvent(NULL, TRUE, FALSE, "MAINEVENT"); hTh[0] = (HANDLE)_beginthreadex(NULL, 0, ThA, NULL, 0, &thID[0]); hTh[1] = (HANDLE)_beginthreadex(NULL, 0, ThB, NULL, 0, &thID[1]); WaitForSingleObject(hEvent[2], INFINITE); for(i=0;i<2;i++) CloseHandle(hTh[i]); return 0; }

  • (マルチスレッド)_beginthreadexに複数の引数を渡す

    現在プログラムでマルチスレッドをやろうとしているのですが、 マルチスレッドの関数に数値や配列などの引数を渡すことは可能でしょうか? MSDNで調べてみると、_beginthreadex関数の4番目のNULLのところに引数リストを 指定できるとあったのですが、その意味が良くわかりませんでした。 以下のプログラムの場合にマルチスレッドに変数a, b, cを引数として渡したい場合は どのように書けばいいのでしょうか? #include <stdio.h> #include <windows.h> #include <process.h> unsigned WINAPI MyThread( void *lpx ){ while (1) { printf("スレッド実行中\n"); Sleep(1000); } return 0; } void main(){ // スレッドに渡したい変数の宣言 int a = 128; int b = 256; int c = 512; // スレッドIDの宣言 DWORD thID; // マルチスレッドの開始 (HANDLE)_beginthreadex( NULL, 0, &MyThread, NULL, 0, (unsigned int*)&thID ); // ループ while (1) { printf("メイン関数実行中\n"); Sleep(2000); } }

  • マルチスレッドについて・・・

    先日マルチスレッドについて質問させていただいたものですが、助言のもと動かしてうまくいったように見えたのですが、ロードしていない部分がありました。 今回はスレッドの中身もかいておきます。 ご助力お願いいたします。 unsigned int WINAPI GameMain::loadthread(void *lpx) { GameMain* gm = (GameMain*)lpx; gm->GInit();   //ロード return 0; } HRESULT GameMain::LoadScreen() { // スレッドの生成 static bool onlyonce_createthread = FALSE; if(onlyonce_createthread ==FALSE) { hTh = (HANDLE)_beginthreadex( NULL, 0, &loadthread, this, 0, (unsigned*)&thID ); onlyonce_createthread =TRUE; } // ローディング画面の描画 static bool loopflg = TRUE; while(loopflg) { int threadCondition = CheckThread( hTh ); switch(threadCondition) { case THREAD_RUNNING: if(graphloaded_flg ==TRUE) //2D画像のロードが終わったら { EnterCriticalSection( &m_criticalSection ); float keep_item = (float)(load_item/MAX_LOAD_ITEM); LeaveCriticalSection( &m_criticalSection ); d2d_control->GaugeDraw(0, 0, keep_item); //画像の描画関数 } break; case THREAD_EXIT: loopflg =FALSE; break; case THREAD_ERROR: return E_FAIL; break; } } float keep_item = (float)(load_item/MAX_LOAD_ITEM); d2d_control->GaugeDraw(0, 0, keep_item);  //画像の描画関数 return S_OK; }

  • マルチスレッドについて

    私はいまマルチスレッドの勉強をしているのですが、ビルドが通るのに実行結果がおかしい状況に陥っています。 ロード画面の処理なのですが、プライマリスレッドでロード画面を描画し、セカンダリスレッドでロード処理を行おうとしています。 問題は、ロードが途中で止まることとロード画面を描画できません。 多分下記の関数が悪いとは思うのですがどうか、ご助力おねがいします。 HRESULT GameMain::LoadScreen() { // スレッドの生成 static bool onlyonce_createthread = FALSE; if(onlyonce_createthread ==FALSE) { hTh = (HANDLE)_beginthreadex( NULL, 0, &loadthread, this, 0, (unsigned*)&thID ); 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 ); float keep_item = (float)(load_item/MAX_LOAD_ITEM); LeaveCriticalSection( &m_criticalSection ); d2d_control->GaugeDraw(0, 0, keep_item); } break; case THREAD_EXIT: loopflg =FALSE; break; case THREAD_ERROR: return E_FAIL; break; } } float keep_item = (float)(load_item/MAX_LOAD_ITEM); d2d_control->GaugeDraw(0, 0, keep_item); return S_OK; }

  • Borland社のコンパイラを使ったDLL

    C++BuilderでDLLファイルを使っておりますが、Vectorコンテナの値をコールバック出来ませんでした。Vectorコンテナを使ったコールバックを可能とする記述方法を教えてください。 Edit1には、思惑通りに102が返ります。Edit2には、1が、Edit3には2が返ります。この例では、201、202を返したいのです。 本体側 #include <vector> ・ ・ extern "C" __declspec(dllimport) int __stdcall test(int*,vector<int>); #pragma package(smart_init) #pragma resource "*.dfm" using namespace std; TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { vector<int> b; b.push_back(1); b.push_back(2); int *a; *a=1; Edit1->Text = IntToStr(test(a,b)); Edit2->Text = IntToStr(b[0]); Edit3->Text = IntToStr(b[1]); b.clear(); dll側 extern "C" __declspec(dllexport) int __stdcall test(int*,vector<int>*);//コンテナの引数の実験 ・ ・ //----------------------------------------------------------------------------- int __stdcall test(int *a,vector<int> &b) { //dllの引数の可能性の実験 for(unsigned int i;i<b.size();i++){ b[i] +=200; } *a+=1; return *a+100; } //-----------------------------------------------------------------------------

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

    マルチスレッドを使ってロード画面を作ろうとしているのですが、 上手く画像が描画更新してくれません。 スレッドの中身は下記の通りです。よろしくお願いします。 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; }

  • 雛形ウィンドウ処理について

    雛形のウィンドウをGetmessage()のところとWndProcのところをトレースしたときに気づいたんですが、 Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, longtThread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartup Function: WinMain(HINSTANCE__ *, HINSTANCE__ *, char *, int), と出るのですが、まぜ順番的( Function: WinMain(HINSTANCE__ *, HINSTANCE__ *, char *, int), Thread: 0x16D4 __tmainCRTStartup Function: WndProc(HWND__ *, unsigned int, unsigned int, long), Thread: 0x16D4 __tmainCRTStartupの交互 ) じゃないのはなぜなのでしょうか?あとFunction: WinMain(HINSTANCE__ *, HINSTANCE__ *, char *, int), Thread: 0x16D4 __tmainCRTStartupと Function: WndProc(HWND__ *, unsigned int, unsigned int, long), に個数に差が出るのですが、なぜなのでしょうか?

  • 親スレッドが子スレッドを監視する方法について(マルチスレッド)

    こんにちは。 私は、A端末から送信されたパケットをB端末で受信し、B端末で受信したそのパケットを再度、A端末へ送信するというプログラムを作成しました。 Phase1.A端末(送信側)→B端末(受信側) Phase2. B端末→A端末 ということです。 上記を実現するために、送信端末において、送信スレッド(親スレッド)と受信スレッド(子スレッド)を立てマルチスレッド処理を行っています。以下にプログラムの概要を示します。 main(int argc ,char *argv[]){ UDPSend(s_port,szServer); } static int UDPSend(unsigned short s_port,char *szServer){ hTh = (HANDLE)_beginthreadex(NULL, 0, UDPReceiveData, NULL, 0, &thID); while((n = fread(send_Buf,1,SEND_DATA_SIZE,fp)) != 0) { sendto(s1, send_Buf, n, 0, (LPSOCKADDR)&addrin1, sizeof(addrin1)); } } unsigned __stdcall UDPReceiveData(void *lpx){ while (1) { size = recvfrom(s2, recv_Buf, (int)sizeof(recv_Buf) - 1, 0, (SOCKADDR *)&from, &fromlen); return 0; } } UDPSend関数にて、パケットをB端末へ送信。UDPReceiveData関数にて、B端末からのパケットを受信しています。この場合、UDPSend関数(スレッド?)がUDPReceiveData関数より先に、終わってしまう場合が生じると思っているのですが。 UDPSend関数がUDPReceiveData関数を監視する方法があるのでしょうか? よろしくお願いします。

  • C++で乱数を重複しないように発生させる

    C++で乱数を重複しないように発生させるようにプログラムを変更しろと言われたのですが、できません。 教えていただきたいです。 #include<iostream> #include<cstdlib> #include<cstring> #include<ctime> using namespace std; int main() { int i,n; int *p; cout<<"何個記憶しますか?"<<endl; cin>>n; p=new int[n]; if(p==NULL){ cout<<"記憶域の確保に失敗しました。"<<endl; return 1; } srand((unsigned)time(NULL)); rand(); i=0; while(i<n){ p[i]=1+(int)((double)rand()/(RAND_MAX+1.0)*75); if(p[i]==p[i]) cout<<"p["<<i<<"]の値"<<p[i]<<endl; i++; } delete[] p; return 0; }

  • 関数内に関数は無理でしょうか? 子スレッド

    CやC++についてまだよくわかっていないのですが、関数内に関数を書くと 「コンパイラ エラー C2601 ローカル関数の定義が正しくありません。」となります。 そこで以下の子スレッドのように関数を関数外に定義して、利用する変数も関数外に定義しました。 変数を外部に定義すると、メモリアクセスになるので、できるだけ避けたかったために 関数内に子スレッドを最初書いていたのですが、何か良い方法はありますか? 子スレッドの引数や戻り値を利用できればいいのですが、そんなことできるのかもわかりません。 あと、関数を配列のように定義できるのでしょうか? test_thread_0, test_thread_1と逐一書くのが面倒です。 int sum1 = 0, sum2 = 0; unsigned __stdcall test_thread_0(void *lpx) // lpxはおまじない? { // 全て足す for (int i = ST1; i <= EN1; i++){ sum1 += Data1[i] * Data2[i]; } return 0; // 正常という意味? } unsigned __stdcall test_thread_1(void *lpx) { // 全て引く for (int i = ST2; i <= EN2; i++){ sum2 -= Data1[i] * Data2[i]; } return 0; }