ダイアログ描画処理
VS2005/MFC/ダイアログベース
宜しくお願いします。
// メイン処理開始
4000行のファイルを開く
一行づつ読み込む
読み込んだデータにデータ処理を行う
途中OnCopyData()関数でダイアログに複数のスレッドを投げる
繰り返す
// メイン処理終り
// スレッドを受信した側の処理(ダイアログ)
スレッドを受けたダイアログ側の処理
ダイアログはスレッドを受けたら一意のスレッドID値により分岐し各処理を行
う
// スレッド処理終り
質問
Text Contorolに投げたスレッドに対する描画処理はピコピコ変化してまめに処理してい
るのですが
List Contorolに投げたスレッドに対する処理はList Contorolがグレイアウトしてしまい
処理が終わった後まともに表示される状況です
単にグラフィックの描画処理がループ処理に対して遅いからこの様な現象が起こるのでし
ょうか?
muutexを使うことで、この様な不具合を解決ができますでしょうか?
スレッドって投げるもの?
冷やかしじゃなくて
スレッドやウィンドウとかの構成がわからん。
4000行のファイル→4000個のスレッドを作る?
この質問読んでも
グラフィックの描画処理とループ処理の
あなたのPCであなたのプログラムの実際の速さは俺には分からない。
ということで推測。
List Contorolの処理が遅いからでしょう。
人間が読めない速度で描画するのは無意味だな。
標準コントロールって速度優先じゃないみたい。
データが増えるほどとんでもなく遅くなったりするし。
List Contorolには前回の再描画から0.1秒経過していない場合は再描画しないとか。
再描画を止めさせる方法があったはず。
そして処理が終わったら再描画する。
List Contorolにデータ追加するときはまとめて10個追加するとか。
1個追加するたびに再描画はおそいじゃん。
そして処理が終わったら残りのデータを追加する。
でも何個が最適なのかはPCの性能やOSのバージョンに依存するからお勧めでない。
/*
スレッドでウィンドウを操作しているならデッドロックに注意。
俺なら以下のように考える。実際これがいい方法かわかりません。
muutexつけたコンテナを用意。
ワーカスレッドはコンテナにデータを溜めていく。
List Contorolのスレッドは0.1秒間隔でコンテナからデータを抜き出し自分に追加。
追加じゃなくて複雑な処理するならこの方法はダメだな。
*/
短い時間にList Contorolに対してたくさん処理するなら
その間一切List Contorolの再描画させない方がいいかもな。
以下雑談
Vistaのエクスプローラで
7万ファイルあるディレクトリとか表示すると
暫定的な内容で表示されて
数秒後にちゃんとした内容に表示しなおすんだよ。
WindowsXPだとフリーズしたかと思うくらい時間かかったはず。
どういう仕組みなんだろう?
というかWindowsXPの時点でやっとけよ。
バックグラウンドで集計して
1秒単位に表示を最新のものに直している感じだった。
すごい感覚的な話だけど。
wclrp ( 'o')さんありがとうございます
>スレッドって投げるもの?
ダイアログに対してSendMessage()やPostMessage()を送出することを職場でよくメッセー
ジを投げると表現するので使いましたよそでは使わないんだ・・・・orz
typedef struct tagDeliveryCount {
int nCount; //他にもデータを持ってますが長くなるので割
愛します
} tagDeliveryCount;
COPYDATASTRUCT cds;
tagDeliveryCount* pDc;
ZeroMemory(&pDc, sizeof(pDc));
totalLen = 0;
totalLen = sizeof(tagDeliveryCount);
pDc = (tagDeliveryCount*)malloc(totalLen);
pDc->nCount = 0;
cds.dwData = COPYDATA_MESSAGE_HWND;
cds.lpData = (void*)pDc;
cds.cbData = (DWORD)totalLen;
CWnd *pOtherWnd = CWnd::FindWindow( NULL, Generator );
if(pOtherWnd){
pOtherWnd->SendMessage(WM_COPYDATA,
(WPARAM)AfxGetApp()->m_pMainWnd->GetSafeHwnd(),
(LPARAM)&cds);
} else {
free(pDs);
throw MyException(MyException::ILLEGAL_ACCESS_EXCEPTION);
}
free(pDc);
こんな感じであるクラス(仮にFoo classとします)からGeneratorという
キャプションを持つダイアログを探して見つかったらデータを送出するという事です。
>スレッドやウィンドウとかの構成がわからん。
スレッドを送出する目的は現在読み込んでいるデータの即値を表示する目的です
故にファイルから一行データを読み込みトークン分割をして構造体に格納して
そのポインタをCOPYDATASTRUCT型のlpData変数に設定して渡しています
なのでスレッドで行っている処理自体は単純なものです。
>4000行のファイル→4000個のスレッドを作る?
一行読み込む毎に3つのスレッドをSendMessage()しています
∴4000×3 = 12,000このSendMessageがダイアログに送出されます。
具体的には
テキストコントロールに送出する文字列
プログレスバーコントロール与える引数
リストコントロールで表示するデータです
>スレッドでウィンドウを操作しているならデッドロックに注意。
どうやらデッドロックが発生している様子です理由はファイル読み込み処理が開始される
と
処理が終わるまでダイアログが固まってしまいます。
>muutexつけたコンテナを用意。
>ワーカスレッドはコンテナにデータを溜めていく。
>List Contorolのスレッドは0.1秒間隔でコンテナからデータを抜き出し自分に追加。
>追加じゃなくて複雑な処理するならこの方法はダメだな。
ごめんなさいmuutex私のミスタイプですMFCなのでCMutex(ミューテックス)でしたね
この方法ためしてみます。
・ワーカスレッドは受信したスレッドを非同期でコンテナに取り込む。
・リストコントロールの描画処理は0.1秒毎のストップウォッチで監視しながら
・同期を取りつつ排他制御を行う。
要するにワーカースレッド(データ受信)と描画処理を切り分けなさいという風に理解し
ました。
> ダイアログに対してSendMessage()やPostMessage()を送出することを職場でよくメッセー
> ジを投げると表現するので使いましたよそでは使わないんだ・・・・orz
メッセージを投げるは通じると思いますが、
あなたはスレッドを投げると書いているのです。
スレッドが(別のスレッドに)メッセージを投げるならわかるのですが。
> >4000行のファイル→4000個のスレッドを作る?
> 一行読み込む毎に3つのスレッドをSendMessage()しています
> ∴4000×3 = 12,000このSendMessageがダイアログに送出されます。
SendMessageで投げられるのはスレッドではなく、メッセージではないですか?
本当に12000のスレッドが作られて投げられていますか?
あなたの言うスレッドとは、CWinThreadかそれを継承したクラスですか?
# サブクラスさんと私との認識がずれているだけならまだよいのですが、
# サブクラスさんがメッセージとスレッドを混同しているならば、
# このプログラムがマルチスレッドであるということも疑わなくてはならない?
用語の使い方がおかしいですね
気を付けます。
スレッドは一切使用してないことをはっきり認識しました
送っているのは3種類のメッセージです。
単純に考えてリストコントロールのアイテムに値を設定して
Updatedata(FALSE);
Sleep(500);
0.5秒待っても処理が終わらないとリストコントロールに値が表示されないんだよね
なんでかな・・・・
> 0.5秒待っても処理が終わらないとリストコントロールに値が表示されないんだよね
> なんでかな・・・・
メッセ-ジはスレッドと違い連続で送られても都合のいい時間間隔(約200ms以上)で
一つ一つ順番に処理されます。
処理に時間が掛かっていると0.5Sで終わらないって事もあるのではないでしょうか。
> 単純に考えてリストコントロールのアイテムに値を設定して
> Updatedata(FALSE);
> Sleep(500);
> 0.5秒待っても処理が終わらないとリストコントロールに値が表示されないんだよね
> なんでかな・・・・
1行分追加するたびに、UpdateDataやSleepを呼んでいるんですよね?
Sleep()の替わりに、m_xcList.UpdateWindow();を1行ごとに呼んだら解決しませんか?
※ m_xcListはリストコントロールのコントロール変数ね。
上の方法で解決するとして、1行毎にUpdateWindowするのは無駄です。
> 人間が読めない速度で描画するのは無意味だな。
とあるように、ストレスを感じない程度の速さで10行毎とか100行毎に描画させてください。
ITO さんたいちう さんありがとうございます
m_xcList.UpdateWindow();
で解決できました。
自分でも調べている時に
VC6 MFCを用いて「仮想リストコントロールで大量データ表示」
http://homepage1.nifty.com/takuhiro/technique/programming/virtuallistctrl/index.
htm
という大変今の私には興味深いHPを見つけたのですが
リストコントロールが 1 件のデータを受け取った場合に、その分のメモリ領域を確保し
ようとしているためである。
処理に遅延が生じるので仮想リストコントロールを用いてキャッシュのような仕組みで
描画処理を行うと高速に大量のデータが描画できるという内容なのですが
一点分からない部分がありますそれは
HP中に
WM_NOTIFTY を 処理する関数 OnGetdispinfo 関数を追加する
とあるのですが
VS2005のList Contorolのプロパティの管理イベントには
OnHdnGetdispinfoList1とOnLvnGetdispinfoList1の二つがあるのですが
いづれも上記のHPのOnGetdispinfo 関数とは引数が異なります
VS2005でWM_NOTIFYを用いてコモンコントロールでイベントが起こった場合
親ダイアログに通知する場合どちらの関数を用いたら良いのでしょうか?
私の手元の環境はVS.NET 2003ですが、次のようにできました。
void CMyDlg::OnLvnGetdispinfoList1(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
pDispInfo->item.pszText = TEXT(ここにデータをセットする);
*pResult = 0;
}
あと、これは描画を速くする方法ではないですよ。
データの追加を速くする方法ですよ。
何が律速段階になっているのか勘違いしないようにしましょう。
補足
最初の質問にあった、グレイアウトするという症状は、
描画に時間がかかっているのではなく、
大量の行追加に時間がかかり、その間描画が後回しに
なるという状況です。多分。
m_xcList.UpdateWindow()を追加することで、
1行挿入したら確実に再描画を行うことで、
描画されていないという状況は解消されます。
ただし、人間が知覚できるより頻繁に描画を更新しても
余計な時間がかかるだけなので、適当な頻度で更新してください。
「仮想リストコントロールで大量データ表示」で書かれているのは、
描画云々ではなく、行追加を速くする方法です。
デフォルト設定では、追加する行の内容によって必要なメモリ量が変わるので、
追加される度にメモリを確保していますが、
コントロールにデータを管理させないことによって、
まとめて行数分のメモリを確保しています。
コントロールはデータを持たないので、必要に応じて
プログラムにデータを問い合わせ、表示をします。
この問い合わせがOnLvnGetdispinfoList1()ですね。
雑談ですが...
wclrp ( 'o')さん
>Vistaのエクスプローラで
>7万ファイルあるディレクトリとか表示すると
>暫定的な内容で表示されて
>数秒後にちゃんとした内容に表示しなおすんだよ。
たいちうさん
>あと、これは描画を速くする方法ではないですよ。
>データの追加を速くする方法ですよ。
Spy++で調べると、Vistaのエクスプローラは
リストコントロール(SysListView32)のスタイルに
LVS_OWNERDATAと、LVS_EX_DOUBLEBUFFER
が指定されてます。
つまり、描画要素はリストコントロール外で持ち、
尚且つスクロール等でちらつきが起きないように
ダブルバッファで描画しているということでしょう。
あと完全に予想ですけど、
IEnumIDList::EnumObjects()後の
IEnumIDList::Next()のループでは、単位件数毎(1000件毎とか?)に
SetItemCountEx()と
UpdateWindow()を行って、長時間のブロッキングを防いでるんではないでしょうか
すいません自己レス
>長時間のブロッキングを防いでるんではないでしょうか
描画はされるにしても、スレッドがブロックすることには変わりないですね^^;
PumpMessage()みたいなことでもしてるのかなぁ...
ドア