Version 14.25
2種類のマルチスレッド
「まずは前回のまとめから」
『まず、スレッドは勝手には増えなくて、 WinMain() からスタートした
メインスレッドの流れは、そのままメッセージループやウィンドウプロ
シージャも通って、最後には WinMain() からも抜けて終了、だよね』
「そういうこと。無理矢理入れ子で書くと、プログラム的にはこういう形に
なっています」
WinMain()
{
MessageLoop()
{
while( 1 )
{
GetMessage();
DispatchMessage() → WndProc()
{
}
}
}
}
『なんかこうみると1関数の普通のプログラムだね』
「それが、メッセージループやメッセージキュー、ウィンドウプロシージャ
が関わってくることで、まるであたかもマルチスレッドになってるんじゃ、
っていう錯覚に陥っちゃうわけ」
『だからウィンドウプロシージャだけ別処理とか思っちゃうんだ……』
「さて、ここまでメインスレッドの流れについて見てきたのは、その問題点
を明確にするためです」
『問題点?』
「つまり」
・ウィンドウプロシージャから返ってこないと固まる。
「ってこと」
『ああ、そんなこと……って、そっか、ウィンドウプロシージャからすぐ
返ってくるとか考えてたら、それわかんないもんね』
「そういうこと。メッセージループとスレッドをちゃんと理解してないとわ
かんない部分だから。そして、この固まるのを避けるために使うのが」
『マルチスレッド!』
「というわけ。スレッドを複数使えば問題ないからね。ところが、ここから
が結構ややこしいんです」
『ここからも、ね』
「マルチスレッドで固まるのを防ぐためにはふたつの方法があります」
『ふたつの方法?』
・ウィンドウプロシージャの先の末端の処理だけをマルチスレッド化する。
(ワーカースレッド)
・メッセージループそのものをマルチスレッド化する
(ユーザーインターフェイススレッド)
「()の中は MFC で使われてる名称。ワーカースレッドと、
ユーザーインターフェイススレッドってこと」
『これってつまり、ワーカースレッドの場合は』
WinMain()
{
MessageLoop()
{
while( 1 )
{
GetMessage();
DispatchMessage() → WndProc()
{
// ここの重い処理を別スレッドで処理する。
// そうすればここからすぐ返る。
}
}
}
}
『ってなるからウィンドウプロシージャで止まることがなくて、逆に
ユーザーインターフェイススレッドって方は……』
WinMain()
{
// この MessageLoop() を複数のスレッドで呼び出す。
MessageLoop()
{
while( 1 )
{
GetMessage();
DispatchMessage() → WndProc()
{
// なら他のスレッドがここで止まっても問題なし?
}
}
}
}
『……なんか自分で言っといてなんだけど違うような気がする』
「ううん、それで基本的なことは合ってるよ。多分イメージがわかないから
かな。たとえば、次のような例を想像してみて」
・ファイル検索処理を開始すると、ダイアログが出る。
ダイアログには【キャンセル】ボタンがあって、押すと検索が止まる。
『検索が〈重い処理〉ね』
「これはワーカースレッド向け。の処理。検索部分だけ別スレッドにすれば
いいから」
『なるほど』
「対して」
・ひとつのアプリケーションで複数の種類のウィンドウがある。
それぞれ別の処理をしていて、同時に表示できる。
「という場合にはユーザーインターフェイススレッドを使います」
『? それって、別にひとつのアプリケーションじゃなくて、えっと……
プロセスいくつも上げればいいんじゃないの?』
「うん、たいがいはそう。そうできない場合……クリティカルセクションを
使うためやファイルロックを掛けるためにプロセスはひとつしかできない」
『げ、そういう理由もあるね』
「それにパフォーマンスの問題もあるかな。アプリひとつ立ち上げるのには
結構な時間が掛かるから、もし頻繁に立ち上げなきゃいけないとなると」
『大変なことになる……スレッドなら早い、ってこと?』
「そういうこと。でも……」
『でも?』
「実は、ユーザーインターフェイススレッドは使わない方がいいんです」
『ええっ!?』
「ユーザーインターフェイススレッドを使わない方がいい理由は、こんな感
じ」
・ MFC のユーザーインターフェイススレッドはすごく複雑で使いづらい。
・ MFC のシステムそのものがマルチスレッドに向いていない。
・同期を取るのが面倒。
・あまり使い道がない。
「実際、ほとんどの場合ワーカースレッドで間に合っちゃうから必要ないん
です」
『確かに、なんか難しそうだし、使い道も良くわからないし……』
「それに、ワーカースレッドでさえ複雑だから、余計複雑になってもわから
ないしね」
『ん? ワーカースレッドでも複雑なの?』
「そうだよ。たとえばさっきの」
・ファイル検索処理を開始すると、ダイアログが出る。
ダイアログには【キャンセル】ボタンがあって、押すと検索が止まる。
「を例に考えてみようか。これにはもうひとつ」
・検索処理が終わるとダイアログが閉じる。
「っていう機能が付きます」
『まぁ普通そうだよね』
「これをさっきのネストの図で書くと……」
WinMain()
{
MessageLoop()
{
while( 1 )
{
GetMessage();
DispatchMessage() → WndProc()
{
// 検索処理を別スレッドで処理する。
// そうすればここからすぐ返る。
// それと、ここにキャンセルボタンが押されたときの
// 処理も記述する。
}
}
}
}
// 別スレッドで呼び出される「検索処理」関数。
SearchFiles()
{
while()
{
// 検索処理中……。
}
}
「こんな感じになります」
『別スレッドで呼ばれるのが SearchFiles() ね』
「さて、ここで3つの問題が出てきます」
・SearchFiles() が終了したらダイアログを閉じる。
・検索して見つかったファイルを表示する。
・キャンセルボタンが押されたら SearchFiles() の処理を止める。
『? そんな難しい? 検索終了したら、って WaitForSingleObject()
使って待てばいいじゃない』
「それしたらダイアログ固まってキャンセル押せなくなるよ?」
『あ”……』
「というわけで次回に続く!」
/*
Preview Next Story!
*/
『ここまでそれほど難しくないなーって思ってたんだけど……』
「マルチスレッドは実践が難しいからね」
『その辺もちゃんと教えてくれるの?』
「もちろん」
『というわけで次回』
< Version 14.26 ファイル検索アプリの準備 >
「につづく!」
『でもさ、この講座ってむしろ実践的なの少なくない?』
「本当に実践的なこと教えてる本なんて皆無だよ……」
『確かに……』