背景
重い処理の進捗状況をダイアログで表示する場合、自分がよくやるのは
- 進捗状況を示すモードレスダイアログを作成
- 重い処理をAfxBeginThread()により別のワーカースレッドとして起動
- モードレスダイアログは適宜タイマによって状況を更新
- モードレスダイアログ上のキャンセルボタンが押された場合はフラグを立てる
- 重い処理内では適宜フラグを監視し必要に応じて処理を中断する
というもの。たいていはこれで問題なく動く。
問題
しかし、この実装だと例えばOnUpdate()にてCTreeViewやCListViewに数十万個のアイテムを追加する場合、フレームワークがOnUpdate()を呼び出している間モードレスダイアログのGUIが無反応・無更新となる問題がある。
原因
この原因は、ワーカースレッドとして作られたスレッドで新たにダイアログを作っても、そのスレッドプロシージャはウインドウプロシージャの一部となってしまうことにある。すなわち、メインウィンドウのウィンドウプロシージャが非常に重い処理を呼び出してしまった場合はモードレスダイアログ上のGUI向けのものも含めウィンドウメッセージがキューにたまり続け、処理されないのである。
2020/05/05追記
より詳しい記事を追加。
解決方法
- 対症療法:重いイベントハンドラ内の処理もマルチスレッド化してしまう
- 根本対策:ダイアログのウィンドウプロシージャをメインウィンドウから独立させる(ワーカースレッドではなく、ユーザーインターフェーススレッドにする)
An example of user interface thread implementation
参考