こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

締切り済みの質問

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

実際のプログラムでバグが出たので、簡易化したプログラムでテストしてみましたがどこが悪いのか分かりませんでした。

#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


どこを直せばいいか教えてください

投稿日時 - 2012-07-01 14:47:19

QNo.7565700

すぐに回答ほしいです

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(4)

ANo.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) );
}
}
...

投稿日時 - 2012-07-01 21:56:37

お礼

わざわざありがとうございます。
tupleという物があるんですね。
知らなかったです

調べてみましたが、どういうものかは分かりましたがプログラムで何をしているのか分からなかったのでそのままコピペしてみました

結果
メインスレッド:0~9
サブスレッド-17891602:0~9
サブスレッド0:0~9
サブスレッド-17891602:0~9
サブスレッド-17891602:0~9
サブスレッド8:0~9
となりました

大体道筋が見えてきたように思います。

投稿日時 - 2012-07-01 23:13:27

ANo.3

zwi

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

投稿日時 - 2012-07-01 17:14:26

補足

あ、申し訳ありません

前略
{
    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

投稿日時 - 2012-07-01 19:11:53

ANo.2

zwi

怪しい所です。
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している。

投稿日時 - 2012-07-01 15:52:50

補足

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

投稿日時 - 2012-07-01 17:02:53

お礼

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

とりあえず、2.3.4.を直してこようと思います

投稿日時 - 2012-07-01 16:29:38

ANo.1

 for( int i=0 ; i<10 ; i++ ) {
  ...
  hTh.push_back( (HANDLE)_beginthreadex(NULL, 0, Login, &i, 0, &thID[i]) );

コロコロ変化している i のポインタを スレッドに引き渡しているのはおかしくない?
スレッドに火が付いた時点で i が変化してるかもですよ?

投稿日時 - 2012-07-01 15:46:06

お礼

なるほど。確かにそうですね

では、どのようにしたらいいのでしょうか?

投稿日時 - 2012-07-01 16:26:20

あなたにオススメの質問