スレッドとシリアル通信と校庭で

amelia2007-05-09

かなり煮詰まった症状が発生していた。

[環境]
GUIメインスレッド - ワーカスレッドの2枚構造で、ワーカスレッド側でシリアル通信を実施している。
ワーカスレッドの起動は開始ボタンハンドラ。
ワーカスレッドの停止は停止ボタンハンドラからスレッド停止フラグを立てて終了イベント待ち。
ワーカスレッド内では適度にスリープが入る。
GUIメインスレッドとワーカスレッドのスレッド優先度は同一。

[症状]
開始ボタンを押下すると、ワーカスレッド起動後、GUIメインスレッドがディスパッチされない。
ゆえに、停止ボタンの操作さえ不可能。
トレースしてみると、ワーカスレッドにてポートオープンした瞬間からワーカスレッド以外にディスパッチされていないような振る舞いになる。
なお、CPU占有率は3%くらい。

[調査]
まず、必要な箇所でポートオープン - プロトコル処理 - ポートクローズという、あまり好ましくない手順で逐次手続きをするよう修正。
結果として、GUIメインスレッドがディスパッチされるようになった。
何故、ポートオープンしただけで、メインスレッド側に実行権が回らなくなるのかは不明。
個人的にはスレッドのライフサイクルでポート制御をしたかったのだが、ひとまず上記実装に。

しかし、停止ボタンを押せるようになったのだが、今度はGUIメインスレッドがキャンセルフラグを立ててイベント待ち(WaitForSingleObject)状態になっても、ワーカスレッドにディスパッチされなくなり、ワーカスレッドが抜けなくなる。よって、停止ボタンハンドラも抜けなくなる。
最初はワーカスレッドからのSendMessage()によるメッセージデッドロックかと調査してみたが、よくよく考えてみれば、SendNotifyMessage()を使っているし、そもそも、メッセージを投げる投げない云々の前からプリエンプションされていないことをログで確認した。ラウンドロビンならば、決してこのような現象は発生しないと思われ、プリエンプションであることを再考したところ、残すはスレッド優先度か?という結論に。
試しに、ワーカスレッドの優先度をGUIメインスレッドより下げてあげると、ワーカスレッドにディスパッチされるようになった。

えぇぇぇぇ

例え、双方のスレッド優先度が同一でも、メインスレッド側はイベント待ちでサスペンドになるんだから、ワーカスレッドはその時点からディスパッチされる(可能性がある)べきであり、かなり納得していない。
そもそも、そのシリアルポートをオープンした状態から、何かの歯車がずれているに違いない。

今のところ、それで動作しているが、これ、ワーカスレッド内でシリアル通信なんかせずに、算出処理などして外部ペリフェラルにアクセスしなければ、スレッド優先度が同一でもスカスカ動作する。つまり、スレッド優先度が問題なのではなく、そのシリアル通信が怪しい。だけど、そのシリアル通信のラッパライブラリはメーカ供給のものだから、ブラックボックス化されていて見れない。

[結論]
ブラックボックスは嫌だな

[おまけ]
VSにて、スレッド起動して、スレッド関数先頭でブレークを打っておいてから、間髪入れずにF10とかのショートカットで連続ステップさせると、時々Windowsごと持っていって、ログオフさえ難しい状態に陥る。マウスでステップアイコンをちこちこっとクリックしていく分には、まずこの現象は発生しない。何なんでしょう。