ダイアログが止まってしまいます

解決


ken  2013-04-08 17:29:54  No: 73663  IP: 192.*.*.*

MFCの開発初心者です。
現在MFCでダイアログベースのアプリを作成しているのですが、
ダイアログが止まってしまいます。
詳細としてはスレッドが二つあり

スレッド1
{
   while (1)
   {
      if(m_a==1)
         m_Flag1 = True;
      else if (m_a == 0)
         m_Flag1 = false;
   
      if(m_b==1)
         m_Flag2 = True;
      else if (m_b == 0)
         m_Flag2 = false;
   

      イベントセット 
      sleep(1);    ※
   }
}

スレッド2
{
   while(1)
   {
      イベント待ち
      if (m_Flag1 == True);
         PostMessage (処理A);

      if (m_Flag2 == True);
         PostMessage (処理B);
   }
}

大まかにはこのような処理をやっているのですが、
例外がでて止まってしまいます。
最終的にはAfxInternalPumpMessageで中断されます。

※のsleepの秒数を伸ばすと止まらないのですが、原因がいまいち分かりません。

PostMessageの中の処理が終わる前にまたPostMessageが呼ばれてしまっているのでしょうか?


visual studioを使ったダイアログ開発は初めてなもので足りない情報などありましたら教えてください。お願いします。

開発はvisual studio 2008です

編集 削除
瀬戸っぷ  2013-04-09 08:26:11  No: 73664  IP: 192.*.*.*

>PostMessageの中の処理が終わる前にまたPostMessageが呼ばれてしまっているのでしょうか?

PostMessage()は、キューにメッセージを積むだけ……です。
ので、「PostMessageの中の処理が終わる前」とかいう状態はないかと。
# おなじメッセージが複数積まれている…という可能性はありますが。

>※のsleepの秒数を伸ばすと止まらないのですが、原因がいまいち分かりません。

sleep()で、他のスレッド(ダイアログのメッセージループ)へ実行権が映りますが、1msでは積まれているキューを全部消化出来なかったのでしょう。

編集 削除
ken  2013-04-09 16:15:09  No: 73665  IP: 192.*.*.*

瀬戸っぷさん回答ありがとうございます。
どうやらおっしゃる通りキューにたまっているメッセージをすべて消化できていなかったのが原因なようです。
原因がはっきりしてよかったです。
現在何とか2つのPostMessageをまとめ、実際1m周期で動くことはないので
4mで回し動作が確認できているのでとりあえずこれで行こうと思います。

ありがとうございました。

編集 削除
tetrapod  2013-04-09 18:42:58  No: 73666  IP: 192.*.*.*

規制されているうちに解決になっている・・・

>      if (m_Flag1 == True);
>         PostMessage (処理A);
たぶん if の行のセミコロンはいらない
っていうかそのせいで、常に PostMessage してるんぢゃないの?

あとこの手のフラグ変数を異スレッドで共有するパターンは結構危険なので注意。
最近の CPU はいろいろ高速化のため小細工しているせいで、共有メモリ上の値は
スレッド1がスレッド2を追い越して実行するように見えるケースがある。

きっちりメモリバリアとか置いて保護したほうが安全だろう。
あとからわけのわからないバグに悩まされずにすむ。

編集 削除
ken  2013-04-10 16:24:27  No: 73667  IP: 192.*.*.*

tetrapodさん回答ありがとうございます。
ifのセミコロンは間違いで書いてしましました、実際のコードにはありませんでした。

メモリバリアとは変数に対して行える処理なのでしょか?
初めて聞いた単語なので調べてみましたがいまいち理解できませんでした(汗

編集 削除
tetrapod  2013-04-10 20:44:57  No: 73668  IP: 192.*.*.*

正しく詳しく説明するには掲示板のスペースではとても足らないが・・・
誤解を招く可能性を承知であえて書くと

out-of-order 実行
・スレッド1で共用変数に書き込む
・スレッド2で共用変数を読む
ようなケースで、
・同一スレッド内ではおよそプログラマの意図順に変数への書き込みがされているように見えるが
・異スレッドでは、書き込み順序が逆に観測される
ようなことが、現代高性能 CPU では普通に行われている。

メモリバリア
ソースコード上で(機械語上で)の変数への書き込みを、
キャッシュメモリや ALU 内キューなどに留めず、実際のメモリに反映させる命令

ということで・・・詳細説明をはしょってしまうが
異スレッドで共有する変数の値を変えてスレッド間同期を取るような場合
・値を0から1にするときには、単なる代入ではなくて InterlockedIncrement を使え
・値を1から0にするときには同様 InterlockedDecrement を使え
ってことだ (x86 の Interlocked**** はメモリバリア機能を内蔵してる)

編集 削除
ken  2013-04-11 10:45:14  No: 73669  IP: 192.*.*.*

tetrapodさん返答ありがとうございます。
メモリバリアについてもう少し詳しく調べてみた所おっしゃっていたことが少し理解できました。  プログラマの意図した通りに動くとは限らず、例えばスレッドAでの書き込み準が a=1 a=2 a=3 の順に行われたのにスレットBでは a=1 a=3 a=2の順に変わったと観測される場合があるということですね、勉強になりました。
InterlockedIncrementについてもとても便利な関数なようで使用させていただきます。。
ありがとうございました。

編集 削除