スレッドで行う処理の限界

[戻る]
猫柳 [E-Mail] 2007/01/31(水) 18:15:04
今勉強中の初心者の者です。お世話になります。
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()内では処理できずに別途関数を呼び出さないといけないの
はなぜでしょうか? 
有効な対策は何でしょう?どなたか教えてください。

とおりすがり 2007/01/31(水) 19:31:24
    dtime = difftime(timer2,timer1); //時間差 dtimeはdouble型です
    TCHAR strTime[10] = _T("123456789") ; // = _T("") ;
    _stprintf_s (strTime, 10, _T("%f"), dtime) ;  
これいがいのコード削ってもリークしてるの?

猫柳 [E-Mail] 2007/01/31(水) 20:38:22
以下の部分を除けばリークはなくなります。
>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) ;
この行を実行したら、リークして、データが壊れてとまってしまいますね。
逆にファイルコピーの部分をはずしてもうまくいきます。
何らかの制限があるんでしょうかね、、、 

超初心者 2007/01/31(水) 20:55:39
変な話だね。
f:\sp\vctools\vc7libs\ship\atlmfc\src\mfc\thrdcore.cpp(306)がメモリ確保を
行ったところってことなのかな?

ITO 2007/01/31(水) 21:35:46
>TCHAR strTime[10]
10確保して、
> _stprintf_s (strTime, 10, _T("%f"), dtime) ;
これを実行するのは危なくないですか。
仮に100位とってみるとか。

猫柳 [E-Mail] 2007/01/31(水) 21:55:33
メモリーリって 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) ;

猫柳 [E-Mail] 2007/02/01(木) 08:45:09
//    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()とかの関数使えないのかな?

ryo 2007/02/01(木) 10:54:33
dtimeがdouble型で、書式フィールドが%fだと小数点以下に「.000000」がつくけど
その分も考慮して文字列確保してありますでしょうか?

 _stprintf (strTime, _T("%d"), (int)dtime) ;
ならランタイムエラーがでないと思うけど

猫柳 [E-Mail] 2007/02/01(木) 13:09:39
ryoさんのおっしゃるとおりです。
(int)dtimeなら出ません。
それで
    size_t a = _tcslen(strTime) + 1;
    _stprintf_s (strTime, a, _T("%9.3f"), dtime) ;
にしてもうまくいきました。(小数点3桁表示)
スレッド以外で動いていたので問題ないと思っていました。
たまたま問題のないところが壊れていただけなんですね。

ryoさん、みなさんありがとうございます。
素人でいろんなところに気を配らんといけないようですね。
スレッド内だからと限界があるわけではないんですね。

PATIO 2007/02/01(木) 13:27:12
突き詰めると単純な話で確保した以上のメモリを使っちゃ駄目って事に
つきると思います。なので今回のように出力結果の桁数が変わりうる場合は
考えうる限りの状況から判断して必要十分な量を確保する必要があると言う事です。
なのでできるだけ出力結果の桁数が限定できるようにプログラムを組むとか
そういう工夫が要ります。

なにが怖いって出力結果の最大桁が予想がつかないなんてのが一番性質が悪いです。

Blue 2007/02/01(木) 13:36:45
_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

猫柳 [E-Mail] 2007/02/01(木) 22:05:28
[[解決]]
>(_stprintf_s(sprintf_sまたはswprintf_s)のtemplate版の関数をコールすることにな
る。)

情報は知っていましたが、書いてることの意味がわかってきました。

PATIOさん、Blueさん、皆さんありがとうございます。

超初心者 2007/02/01(木) 23:07:28
_stprintf_sってバッファのサイズを指定するから
バッファ不足だとしたらすぐにわかるかと思っていた。
少なくともメモリリークになるのが気になる。

ryo 2007/02/02(金) 09:25:55
メモリーリークするのは
バッファぶっ壊しちゃってるため
スレッドが終了するときに
正しくメモリーを解放できなかったからではないかな

Ban 2007/02/02(金) 10:34:32
> 正しくメモリーを解放できなかったからではないかな

多分そうですね。

# バッファ壊した時点で、以後の動作の結果に正しい意味なんてないですし。

tetrapod 2007/02/02(金) 10:54:41
んぢゃなくて _stprintf_s はバッファオーバーフローしないはずなんだけど。
バッファオーバーフロー検出時の挙動ってどうなんだっけ?
俺は VS2005 使ってないから知らないや。
MSDN2 によれば invalid parameter handler が挙動するとあるけど。

バッファオーバーフロー検出で即死→
正常終了シーケンスを通らないのにリークチェッカ起動→
リークしてる誤報告
なのでは?

Blue 2007/02/02(金) 11:06:45
>_stprintf_sってバッファのサイズを指定するから
>バッファ不足だとしたらすぐにわかるかと思っていた。

>_stprintf_s はバッファオーバーフローしないはずなんだけど。
私もそう思った。

_stprintf_sのtemplate版でないほうで、第二引数で確保しているバッファのサイズ
よりも大きい値を指定するとだめそうだけど。

TCHAR buff[10];
_stprintf_s(buff, 11, _T("%d"), 0);

こんなヤツ。

だけど、示されたコードでは大丈夫なはずなんだけれどね、、、
(戻り値として -1 が返ってきそうだけど。)

Ban 2007/02/02(金) 12:01:53
> んぢゃなくて _stprintf_s はバッファオーバーフローしないはずなんだけど。

御意…orz

# _sは使わないからなぁ。

PATIO 2007/02/02(金) 17:36:13
うーん、わかんなくなってきたぞ。
_stprintf_sの解説では設定可能な最大文字数とあるんですよね。
で、文字列の最後に'\0'を付加する事も書いてあるんですが、
この最後の'\0'って文字数に含まれてるんですかねぇ?
これが含まれないなら要素数10の配列には9しか設定できない
計算になるような気がするんですけれど。
そうなるとMSDNの使用例はおかしいって話になりますよねぇ。
うーん。(--;

古いスレッドには追加発言できません。