Version 14.30
スレッドを途中で止める
「前回はとりあえずスレッドで検索するようにしました」
『マルチスレッドになったからダイアログとか固まらなくなったけど、でも
まだまだだよね』
「具体的には、次の機能がまだ残っています」
・検索対象フォルダの表示
・検索結果の表示
・キャンセル機能
・検索終了時にキャンセルダイアログを閉じる機能
「今回はこのうち」
・キャンセル機能
「を作ってみます」
『おお』
「さて、まずはコードから。以前のコードをちょこっと修正します」
// ストッパー。
bool g_bIsStop = false;
// このストッパーをオンにする関数。
void SetStopOn()
{
g_bIsStop = true;
}
/*
指定したフォルダからファイルを検索します。
戻り値として一致数を返します。
*/
int CountMatchFile
( const char * const p_pchFolderPath
, const char * const p_pchFileName
)
{
int iNum = 0;
// ストップポイント1。
if( g_bIsStop == true )
{
return 0;
}
// 検索するフォルダをカレントディレクトリにします。
// 略...
do
{
// ストップポイント2。
if( g_bIsStop == true )
{
return iNum;
}
// 略...
return iNum;
}
// StartCountMatchFile() と CSearchDlgDlg::OnSearchStart() は
// 前回と同じ。
『だいぶはしょってるね』
「全部載せるとかなり長いから……〈略〉のところは前回のコードを参考に
してください」
『で、具体的にはどこが変わってるの?』
「まず、ストッパーを追加しました」
// ストッパー。
bool g_bIsStop = false;
「この変数が false の間だけ検索する、ってします。この変数が true に
なったら検索をストップします」
『? こんなんでいいの?』
「いいんです。実は、スレッドを〈強制的に止める〉のは良くないんです」
『え、そうなの?』
「プログラムが動いているのを無理矢理止めるようなもんだからね」
『う、それはヤバそう……』
「だから、プログラムの方でこういうフラグを用意して、プログラムの中で
普通にスレッドを終わらせるようにするんです」
『普通にスレッドを終わらせる?』
「そう。たとえば」
// ストップポイント1。
if( g_bIsStop == true )
{
return 0;
}
『あ、 true なら関数から抜ける……あ、そっか、別スレッドで呼び出した
関数から抜けたら、そのスレッドもなくなるんだもんね』
「そういうこと。スレッドを途中で止める、ってことは、本当の意味で止め
るんじゃなくて、スレッドで呼び出した関数を、何もせずにすぐに終わらせ
るってことなんです」
『……それって難しくない? 今回はループしたり再帰呼び出ししてるから
いいけど、そういうのじゃなくて、たとえばネットワークとかなんとか?』
「そうだね、他の関数を呼び出して、その関数がすごく時間掛かる場合には
この方法じゃ無理だね……でも強制的に止めるのはやっぱり危険」
『むー、できるだけこの方法でってことね……』
「このフラグチェックの方法が一番簡単だからね。今回は、これを2箇所で
しています」
『関数の頭と、検索中のループでね。再帰呼び出しのループではしなくてい
いの?』
「そっちはすぐ関数を呼び出すわけだから、その関数の頭のチェックで」
『そか、そこですぐ返ってくからいいんだ。あ、あとこれ』
// このストッパーをオンにする関数。
void SetStopOn()
{
g_bIsStop = true;
}
『これなに? 使ってないんだけど』
「これから使うんです。キャンセルボタンがあるダイアログは?」
『検索中ダイアログ』
「そのダイアログを操作するクラスは?」
『 CSearchingDlg クラス』
「そのクラスがあるファイルは?」
『? SearchingDlg.cpp とか SearchingDlg.h のこと?』
「そう。で、今コード書いたファイルは?」
『 SearchDlgDlg.cpp だよ』
「そう、最初に表示されるダイアログだからね」
『……で?』
「で、キャンセルボタンが押されたときにストッパーをオンにしなきゃいけ
ないでしょ。それってつまり、 SearchingDlg.cpp のイベントハンドラから
SearchDlgDlg.cpp の g_bIsStop に値をセットしなきゃいけないってこと」
『……あ”、ファイル違うとアクセスできないんだっけ』
「ってゆーかその辺ってちゃんとはまだ教えてないからね……一応、
Version 3.22 ( No.047 ) で教えてるんだけど」
『ん? どのへん?』
「この中で」
CCalcApp theApp;
// のあとに、
CCalcApp& GetTheApp()
{
return theApp;
}
// と書き足してください。
『あ、今回のと似てる』
「状況が同じだからね。この時は、他のファイルから theApp にアクセスす
る方法の説明」
『あー、だから関数作ってたんだね……こんときは、確かヘッダーファイル
の方に』
// この下の行を追加。
CCalcApp &GetTheApp();
『って追加したんだ』
「というわけで、今回も同じようにします。 SetStopOn() は
SearchDlgDlg.cpp にあるから、そのヘッダーファイル SearchDlgDlg.h に
次のコードを追加します」
class CSearchDlgDlg : public CDialog
{
// 略...
};
// この下の行を追加。
void SetStopOn();
「これで、他のファイルから SetStopOn() を呼べるようになります」
『おー』
「次に、キャンセルボタンのイベントハンドラを追加します。【検索中】
ダイアログの【キャンセル】ボタンで右クリック、メニューから
【イベント】を選んで、ダイアログで【追加と編集】を選んで」
『いつものイベントハンドラ作るのだね。【メンバ関数の追加】ダイアログ
で【OnCancel】って表示されてる。そのままOKしちゃうね』
「うん。そのイベントハンドラで」
void CSearchingDlg::OnCancel()
{
// ストップさせます。
SetStopOn();
CDialog::OnCancel();
}
『お、呼び出してる呼び出してる!』
「これを呼び出せば g_bIsStop が true になります。ただし、あとひとつ
修正が必要です」
『え、どこ?』
「ヘッダーファイルの追加。このイベントハンドラがある SearchingDlg.cpp
の上の方で SearchDlgDlg.h のインクルードををする行を追加してください」
// SearchingDlg.cpp : インプリメンテーション ファイル
//
#include "stdafx.h"
#include "SearchDlg.h"
// 下の行を追加。
#include "SearchDlgDlg.h"
// 上の行を追加。
#include "SearchingDlg.h"
「これをしないと、さっき追加した」
void SetStopOn();
「が見えないから」
『う、めんどい……』
「まぁ、ちゃんと説明してないからね……とりあえず、これでどう?」
『あ、うん。ビルドして実行、検索開始……キャンセルっ! お! ちゃん
とキャンセルできた!』
「フラグを用意して、スレッドで呼び出す関数の中でそれをチェック、
キャンセルボタンが押したときにそのフラグを立てる」
『仕組みとしては結構簡単ねー』
/*
Preview Next Story!
*/
『やっとキャンセル機能ができたね。次はどれ?』
「次は残り全部」
『え、3つ全部?』
「そう。でも次の次の回」
『じゃあ次回は?』
「コードだけ」
『げ』
「というわけで次回」
< Version 14.31 スレッドからダイアログに表示する >
『につづく!』
「来週は読まなくてもいいかも」
『それもどうかな……』