VC++6.0(SP2), WinXp
m_pThread = AfxBeginThread(hogehoge, this);
によりスレッドの生成を行っています。
m_pThreadは、NULLにはならず成功しているように思われます。
しかし生成の次の行で、m_pThread->m_nThreadIDをデバッグ出力すると、0xdddddddddと
なり(初期化されていないのでデバッガが0xdddddddddを表示?)スレッドが正常に作成
されていないように思われることが時々発生します。(実際そのスレッドは動いていな
い)
試験のために、ただループでAfxBeginThread、WaitForSingleObject(タイムアウト付)を
呼ぶプロジェクトを作成し、スレッド内では呼び出し回数をデバッグ出力するだけとい
うものを作成してみましたが、こちらでも1回しか上記現象は発生しなかったので発生
頻度は格段に少ないものの発生しました。
うまく動いている時は
AfxBeginThreadの次の行では0xddddddddd以外の値、
WaitForSingleObjectの次の行では0xddddddddd
となります。
原因、対策等ご存知の方いらっしゃいませんか?
追記情報です。
スレッド生成だけのプロジェクトでは、現象が再現しないため本目的のアプリを使って
デバッグをしています。
ただし最終目的は、スレッドの中でスレッド生成なのですが、入れ子にしない状態でも
現象がでいてたので、現在は入れ子にしない状態でデバッグしています。
またスレッド内部の処理を呼び出し回数をカウントし500msecスリープするだけに変更し
てあります。
すると、m_nThreadIDが正しく取れているにも関わらず、スレッド内の処理が動いていな
い場合がある(カウントされない)ことがわかりました。
試しにカウンタはvolatileをつけて宣言しても状況変わりませんでした。
次に、スレッド内の処理をスリープ後にカウントするようにしたら正常に動作している
ようです。(カウントされている)
なお、現在当初の問題であった生成時m_nThreadIDが不正になる現象は発生していませ
ん。
windowsでスレッドを使ったプログラムの経験がなく、スレッドを利用する際の注意点な
ども合わせてアドバイスいただけると助かります。
よろしくお願いいたします。
よくあるのはレースコンディションを想定していないバグ
---生成側スレッド
pTh=AfxBeginThread(...);
AfxBeginThread の中で子スレッドが生成され、
AfxBeginThread から呼び出し元に帰る前に直ちにそちらに処理が移る
---生成されたスレッド
(バグ等により)終了条件が既に満たされているので直ちに終了する
pTh に返されるべきスレッドオブジェクトが delete される
---生成側スレッド
AfxBeginThread から帰ってきたときには既に子スレッドは終了済みであり、
よってスレッドオブジェクトは既に delete 済みであるためアクセスエラー。
m_pThread->m_nThreadID をアクセスする必要があるのであれば *必ず*
m_bAutoDelete=TRUE にしなければならない。そのためには *必ず*
CREATE_SUSPENDED でスレッド生成しなければならない。
参照
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+201004/10040026.txt
あ、ごめん m_bAutoDelete=FALSE のまちがいね。
具体的なコードもないので感触だけですが(あってもVC6だと再現確認できませんが…)、
「(スレッドの作りすぎ等で)生成に失敗しただけ」というオチはありませんか。
それ以外は、デバッガの表示上の問題か、確認手段に問題があるだけのような気も…
↓それ以外で想定されそうな?例
スレッドを起動した瞬間にそのスレッドが動き出すとは限らない
(起動する方のスレッドがまだ動き続けることもある)わけで、
例えば、「起動はかけたけどまだ実際には動いていない(カウントしていない)」
状態でデバッガがブレイクしているだけということはありませんか。
(ステップ実行などしてる場合は特に起きやすいかと)
また、「カウントする変数」はどのように定義していますか。
基本的なことですが、もしも同じ変数を各スレッドでカウントアップしているなら、
排他制御(例えばInterlockedIncrementを使う等)はされていますか。
tetrapodさん、レスありがとうございます。
>AfxBeginThread から帰ってきたときには既に子スレッドは終了済みであり、
>よってスレッドオブジェクトは既に delete 済みであるためアクセスエラー。
なるほど、そうかもしれません。
そういう目でプログラムを見たら、変数の設定位置に問題があるような気もしてきまし
た。
>m_pThread->m_nThreadID をアクセスする必要があるのであれば *必ず*
>m_bAutoDelete=TRUE にしなければならない。そのためには *必ず*
>CREATE_SUSPENDED でスレッド生成しなければならない。
m_pThread->m_nThreadIDは、デバッグ表示以外利用していません。
> http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+201004/10040026.txt
なんとなく解りますが、もう少し良く読まないと・・・・
もうちょっと試してみます。
Banさん、レスありがとうございます。
>「(スレッドの作りすぎ等で)生成に失敗しただけ」というオチはありませんか。
ループ中でのスレッド作成は、1つのスレッドが終わったら次のスレッドを作成するよ
うにしています。
>スレッドを起動した瞬間にそのスレッドが動き出すとは限らない
そのような目でソース見直し中です。
変数の設定タイミングに問題があるように思えてきました。
引き続き、デバッグしてみます。
複数のスレッドを起動し、操作するためにはイベント・ミューテックス(あってる?)
等をつかって排他制御考えないとだめだと思います。
まずは、スレッドを二個以下にしてうまくいくか試してみたらどうですか?
ITOさん、レスありがとうございます。
>複数のスレッドを起動し、操作するためにはイベント・ミューテックス(あって
る?)
>等をつかって排他制御考えないとだめだと思います。
資源の共有(排他を考慮しなければならないようなもの)は、複数のスレッドでTCP/IP
を利用しているだけです。
しかし異なるソケットを使用しているので排他制御は行っていませんが、大丈夫でしょ
うか?(ソケットが異なるので排他は不要と理解しているのですが・・・)
一応、先程みなさまのアドバイスを参考に、処理の順番を確認したところ、スレッド生
成後の処理が走る前にスレッドが動いてしまうとマズイ所がありましたので、修正し現
在は問題なく動いているように思われます。
> 資源の共有(排他を考慮しなければならないようなもの)は、複数のスレッドで
TCP/IP
> を利用しているだけです。
> しかし異なるソケットを使用しているので排他制御は行っていませんが、大丈夫でし
ょ
> うか?(ソケットが異なるので排他は不要と理解しているのですが・・・)
「異なるソケットを使う」というのも排他制御の一つの考え方だと思います。
あとは、複数のスレッドの操作ですね。数が決まっていれば「WaitForEvent」等で
他のスレッドを待たせるが出来ますが、決まってないとなるとそれなりに難しいです。
データの読書き部分をクリティカル セクション等で制御するとか必要なのでは?
データの個数が決まっていて、すべて配列変数等で区分けされているならば別です。
特に、ネットワーク通信は送受のタイミングがあまり決まってませんよね。
ITOさん、レスありがとうございます。
>データの読書き部分をクリティカル セクション等で制御するとか必要なのでは?
クリティカルセッションですか。
調べてみます。