今勉強中の初心者の者です。お世話になります。
Visual Studio2005(MFC)でダイアログベースのスレッドを使ったプログラムを作成して
います。
サイズの大きなファイルをcopyしながらプログラスバーを更新し、処理時間を計測する
プログラムです。
前回”MFCのスレッド実行中のコマンド処理“(2007/1/26)で質問で改善したものに時
間計測の処理を加えたものです。
void CnewoneDlg:: copyfile()
{
//省略(ファイルを256バイトづつコピーするループ処理)
// 処 理時間を算出し、時間表示
//終了時間計測
dtime = difftime(timer2,timer1); //時間差 dtimeはdouble型です
TCHAR strTime[10] = _T(123456789) ; // = _T(") ;
_stprintf_s (strTime, 10, _T(%f), dtime) ;
//上が問題 文字列変換します。
// スレッド終了処理。
// ボタン禁止解除
}
UINT CnewoneDlg::CopyThread( LPVOID pParam )
{ //threadからcopyプログラムを呼び出し
// copyfile()の呼び出し
}
void CnewoneDlg::OnBnClickedButton1()
{//ボタンコマンド
// スレッド開始
m_pCopythread = AfxBeginThread( CopyThread, this );//
}
どうも _stprintf_s (…..)を実行するメモリリークすることがわかりました。
Detected memory leaks!
Dumping objects ->
f:\sp\vctools\vc7libs\ship\atlmfc\src\mfc\thrdcore.cpp(306) : {124} client
block at 0x003A9890, subtype c0, 68 bytes long.
スレッドの外でテストしたときは問題がなかったです。
Newで割り当てしていないのになぜかと思いましたが、スレッド内できる何らかの限界が
あるんでしょうか?
また、 スレッドCopyThread()内では処理できずに別途関数を呼び出さないといけないの
はなぜでしょうか?
有効な対策は何でしょう?どなたか教えてください。
dtime = difftime(timer2,timer1); //時間差 dtimeはdouble型です
TCHAR strTime[10] = _T(123456789) ; // = _T(") ;
_stprintf_s (strTime, 10, _T(%f), dtime) ;
これいがいのコード削ってもリークしてるの?
以下の部分を除けばリークはなくなります。
>dtime = difftime(timer2,timer1); //時間差 dtimeはdouble型です
> TCHAR strTime[10] = _T(123456789) ; // = _T(") ;
> _stprintf_s (strTime, 10, _T(%f), dtime) ;
とくに、
> _stprintf_s (strTime, 10, _T(%f), dtime) ;
この行を実行したら、リークして、データが壊れてとまってしまいますね。
逆にファイルコピーの部分をはずしてもうまくいきます。
何らかの制限があるんでしょうかね、、、
変な話だね。
f:\sp\vctools\vc7libs\ship\atlmfc\src\mfc\thrdcore.cpp(306)がメモリ確保を
行ったところってことなのかな?
>TCHAR strTime[10]
10確保して、
> _stprintf_s (strTime, 10, _T(%f), dtime) ;
これを実行するのは危なくないですか。
仮に100位とってみるとか。
メモリーリって newで動的に割り当ててデリートしてなかったときに発生するのかの思
っているんですが、ほかにも発生することがあるんですかね。
Dumping objects ->
f:\sp\vctools\vc7libs\ship\atlmfc\src\mfc\thrdcore.cpp(306) : {124} client
block at 0x003A9890, subtype c0, 68 bytes long.
私には上のメッセージではどうしたらいいのかわからんです。
ファイルコピーの部分は次の通りです。(openはスレッド起動する前にしてます。)
DWORD nRead = 0, nWriten = 0;
char buff[256];
j = filesize;
while(j > 0){
ReadFile (hf1, buff, sizeof(buff), &nRead, NULL);
WriteFile(hf2, buff, nRead, &nWriten, NULL) ;
j -= nRead ;
}
CloseHandle(hf1) ;
CloseHandle(hf2) ;
// TCHAR strTime[10] = _T(123456789) ; // = _T(") ;
// size_t a = _tcslen(strTime) + 1;
// _stprintf_s (strTime, 10, _T(%f), dtime) ;
上の部分を次のようにCstring文字列に変えたらうまくいきましたね。
CString strTime ;
dtime = difftime(timer2,timer1);
strTime.Format(_T(%f),dtime) ;
また、実数を
_stprintf (strTime, _T(%f), dtime) ;
とするとRuntime errorでdata破損が出ています。
_stprintf_sの部分が怪しいようです。特に第二引数の部分が関連していると感じます。
文字数は10もじにしているのですが、ためしに20文字にしても同じようです。
今日現象は違うけど、同じことのようです。
MFCのスレッド内で_stprintf_s()とかの関数使えないのかな?
dtimeがdouble型で、書式フィールドが%fだと小数点以下に「.000000」がつくけど
その分も考慮して文字列確保してありますでしょうか?
_stprintf (strTime, _T(%d), (int)dtime) ;
ならランタイムエラーがでないと思うけど
ryoさんのおっしゃるとおりです。
(int)dtimeなら出ません。
それで
size_t a = _tcslen(strTime) + 1;
_stprintf_s (strTime, a, _T(%9.3f), dtime) ;
にしてもうまくいきました。(小数点3桁表示)
スレッド以外で動いていたので問題ないと思っていました。
たまたま問題のないところが壊れていただけなんですね。
ryoさん、みなさんありがとうございます。
素人でいろんなところに気を配らんといけないようですね。
スレッド内だからと限界があるわけではないんですね。
突き詰めると単純な話で確保した以上のメモリを使っちゃ駄目って事に
つきると思います。なので今回のように出力結果の桁数が変わりうる場合は
考えうる限りの状況から判断して必要十分な量を確保する必要があると言う事です。
なのでできるだけ出力結果の桁数が限定できるようにプログラムを組むとか
そういう工夫が要ります。
なにが怖いって出力結果の最大桁が予想がつかないなんてのが一番性質が悪いです。
_stprintf_sですが、C++でコンパイルしていて
第一引数がTCHAR型配列である変数を指定する場合、バッファの指定はなくても動きま
す。
_stprintf_s(strTime, _T(%9.3f), dtime);
(_stprintf_s(sprintf_sまたはswprintf_s)のtemplate版の関数をコールすることにな
る。)
[MSDN]sprintf_s、_sprintf_s_l、swprintf_s、_swprintf_s_l
http://msdn2.microsoft.com/ja-jp/library/ce3zzk1k(VS.80).aspx
>(_stprintf_s(sprintf_sまたはswprintf_s)のtemplate版の関数をコールすることにな
る。)
情報は知っていましたが、書いてることの意味がわかってきました。
PATIOさん、Blueさん、皆さんありがとうございます。
_stprintf_sってバッファのサイズを指定するから
バッファ不足だとしたらすぐにわかるかと思っていた。
少なくともメモリリークになるのが気になる。
メモリーリークするのは
バッファぶっ壊しちゃってるため
スレッドが終了するときに
正しくメモリーを解放できなかったからではないかな
> 正しくメモリーを解放できなかったからではないかな
多分そうですね。
# バッファ壊した時点で、以後の動作の結果に正しい意味なんてないですし。