[MFC]マルチスレッド2
マルチスレッドその2
前回 はマルチスレッドでは、スレッド終了前にメイン処理が終了するとメモリリークが
発生するところまで。
で、これを解消するためには、メイン処理が終了する前にスレッド処理が終了するのを
待つ(同期する)必要がある。
// スレッド関数
UINT ThreadFunc(LPVOID param)
{
printf("スレッド起動\n");
Sleep(1000);
printf("スレッド終了\n");
return 0;
}
// メイン処理
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
printf("メイン開始\n");
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
・・・中略
}
else
{
CWinThread *pThread = AfxBeginThread(ThreadFunc, NULL);
if (::WaitForSingleObject(pThread->m_hThread, INFINITE) == WAIT_FAILED)
{
printf("wait error\n");
}
}
printf("メイン終了\n");
return nRetCode;
}
スレッドの終了を同期するために、WaitForSingleObject関数を使う。
コレは、第一引数にスレッドのハンドルを指定し、第二引数にタイムアウト時間を
設定することで、指定時間指定スレッドの終了を待つものである。
第一引数のスレッドハンドルは、スレッド生成時に受け取ったCWinThread
のメンバ変数 m_hThread を指定すればよい。
第二引数にINFINITEを指定すると、タイムアウト時間は無制限つまり
スレッドが終了するまで待ち続けるということになる。
実際に実行した際の結果はこちら。
[結果]
> メイン開始
> スレッド起動
> スレッド終了
> メイン終了
今回はきちんとスレッドの起動と終了がメインの間に入っている。
通常であれば、コンソール画面も一瞬で消えてしまう程度のコードだが、
スレッド関数内の1秒スリープがあるため、コンソールも1秒ちょっと表示される。
また、メモリリークも解消された。
しかし、今度は次のような問題が出てくる。
WaitForSingleObject で待とうとしているスレッド関数が、待ち処理に入る前に
すでに終了していた場合はどうなるだろうか。
例えば、メイン処理のWaitForSingleObjectの直前に 2秒のスリープを
入れてみた場合、結果は以下のようになる。
[結果]
> メイン開始
> スレッド起動
> スレッド終了
> wait error
> メイン終了
上記のように、WaitForSingleObjectに失敗する。
この現象の原因と対処はまた次回。
[MFC]マルチスレッド1
マルチスレッド
[環境]
VS2008
WIN32コンソールアプリ(MFC共通ヘッダ追加)
スレッドとはCPU利用の単位のこと。
1つのプロセス(今回の例ではコンソールアプリ)の中に1つ以上のスレッドを持つ。
このスレッドが同一プロセス内で複数もって処理を行うことが
マルチスレッドとなる。
マルチスレッドの用法としては、規模の大きい計算などをメインの処理から分岐させ、
バックグラウンドで非同期に処理を行わせるイメージ。
(実際はちょっと意味合いは違う。イメージ。)
組込などのRTOSなどでも使われ方が多少違ってくると思う。
以下、簡単なスレッドの生成と、その流れをコードで。
スレッドの種類にはワーカースレッドとユーザーインターフェーススレッド
があるが、今回はワーカースレッドについて。
// スレッド関数
UINT ThreadFunc(LPVOID param)
{
printf("スレッド起動\n");
Sleep(1000);
printf("スレッド終了\n");
return 0;
}
// メイン処理
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
printf("メイン開始\n");
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
・・・中略
}
else
{
CWinThread *pThread = AfxBeginThread(ThreadFunc, NULL);
}
printf("メイン終了\n");
return nRetCode;
}
main からAfxBeginThread()関数を呼び、スレッドを生成している。
渡す引数は、実行するスレッド用の関数のポインタと、それに渡すパラメータ
を指定すればとりあえずOK。
AfxBeginThread はCWinThread 型のポインタを返すので、受け取る。
(今回は特に使用しない。)
今回はThreadFuncという作成したスレッド用関数と、その関数の引数となる
値だけを渡している。
(上記例は、スレッド用関数は引数が不要なのでNULLとしている。
第2引数はvoid型のポインタである必要があるので、実際に引数を
使用する際は、元の型にキャストするなどして使用する。)
スレッド関数で行っていることは、処理の突入/終了を表示することと、
その間に1秒間のスリープをしているだけである。
先に述べたように、メインの処理とは非同期で処理を行うので、上記コードを
実行すると次のような結果となる。
(処理速度によっては変わることもあると思う。)
[結果]
> メイン開始
> メイン終了
> スレッド起動
このように、非同期で処理が進むためというのと、メイン処理がシンプルすぎることも
あってか、メイン処理が終わってからやっとスレッドが起動している。
しかも、スレッド終了が呼ばれていない。
この時どうなるかというと、メインはすでに終了しているのにスレッドだけが動いている
ということで、メモリリークが発生する。
つまり、これに対応するには、メインが終了する前にスレッドの終了を
待つ必要がでてくる。
対応方法については次回 。
[MFC]簡易版split系関数
MFCアプリからカンマ区切りの数値データを読み込んで、配列に格納。
[環境]
VS2008
WIN32コンソールアプリ(MFC共通ヘッダ追加)
テキスト読み込みについてはこちら
サンプルデータ
[test.txt]
1,2,3
4,5,6
※ 行データに不正な値(「,」をはさんで何もないとか)でない、かつ列固定の場合
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
・・・中略
}
else
{
CStdioFile file(_T("C:\\test.txt"), CStdioFile::modeRead);
CString str;
while(file.ReadString(str))
{
int split[3] = {0};
TCHAR *next;
TCHAR *token = wcstok_s(str.GetBuffer(), _T(","), &next);
int i = 0;
do
{
split[i] = _wtoi(token);
token = wcstok_s(NULL, _T(","), &next);
i++;
} while(token != NULL);
}
}
return nRetCode;
}
wcstok_s でデリミタ(区切り文字)までの文字を読み込んで、TCHAR型のtokenに格納。
格納した文字列を _wtoi で数値変換。
ループ内でのwcstok_s の第一引数がNULLが何故かは知らない。
MSDNを参考にしたので。
もっと可変長データを扱いたいとかあったら、VectorなりCArrayなり使うと
いけるんじゃないかな。
今必要なのはコレで十分だからここまで。
Perlだとこんなの一瞬なのに、Cは文字列系のサポート弱いのなんの。
[MFC]テキストの読み込み
MFCアプリからテキストを読み込んで、コンソール表示する。
[環境]
VS2008
WIN32コンソールアプリ(MFC共通ヘッダ追加)
読み込むテキストとして
[test.txt]
123
456
というテストデータを用意した。
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
// MFC を初期化して、エラーの場合は結果を印刷します。
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
・・・中略
}
else
{
// ファイルオープン
CStdioFile file(_T("C:\\test.txt"), CStdioFile::modeRead);
CString str;
while(file.ReadString(str))
{
_tprintf(str.GetBuffer());
_tprintf(_T("\n"));
}
}
return nRetCode;
}
CStdioFileクラスのReadStringメソッドは読み込んだファイルを1行読み込む。
MFCはCの原始的な処理は意外と苦手っぽいきがする。
それにしてもマルチバイト文字列は大嫌いです。