グッドデザイン賞を受賞したウォーターサーバー >>

現在,画像データの変換ソフトを作っており,変換計算中に,変換されたファイルの経過状態を示すためにプログレスバーをつけております.
プログレスバーは,経過状態に合わせて正常に動作するのですが,別のウィンドウ等に切り替えたりした時にプログレスバーの動作が止まってしまい,変換が終了したときにプログレスバーの終了状態だけが表示されてしまいます.
そこで,質問なのですが,どのようにしたらウィンドウを切り替えたときなどにもプログレスバーが再描画されるでしょうか?
現在,変換計算とプログレスバーを別スレッドにはしていないのですがそれが原因なのでしょうか.

現在の環境はWindowsXP VisualC++6.0です.
ご教授お願いいたします.

A 回答 (2件)

>Keisan()とMyThread()が同時に処理されると思ったのですが


実行環境が「マルチコアCPU」ではない場合、Keisan()とMyThread()は「1つのCPUが順に実行」します。複数のスレッドは同時には処理されません。

「複数のスレッドが時分割で高速に切り替わり、まるで同時に処理されているように見えるだけ」です。

しかも「複数のスレッドが時分割で高速に切り替わ」る為には「カーネルによりスレッドの切り替えが行われる必要」があります。

「カーネルによるスレッド切り替え」を発生させるには「API関数を呼ぶか、イベントハンドラ関数からリターンするなどで、制御がカーネルに移る」必要があります。

>Sleep(100);
Sleepは「プロセス全体」を停止させます。そして、この関数を呼んでもカーネルには制御が移らずスレッド切り替えは発生しません。

「単にSleep(100)を10回繰り返す」だけではカーネルに制御が渡らないのでスレッドは切り替わらず、Keisan()スレッドがCPUを占有します。

Windowsでは、このような「特定の関数内でループして長時間CPUを独占し、なかなか関数から抜けない」という書き方をすると、他のスレッド、他のプロセス、他のアプリが沈黙してしまう場合があります。

「動いているCPUは1つだけ」なので、誰かが一人占めすると、他は「後回し」にされてしまいます。

長時間のループを行う場合は、ループの途中で、他のスレッド、他のタスク、他のプロセスに制御を渡し、CPUを占有してはいけません。
    • good
    • 0
この回答へのお礼

>長時間のループを行う場合は、ループの途中で、他のスレッド、他のタスク、他のプロセスに制御を渡し、CPUを占有してはいけません。

なるほどですね.
ということは,「何かの計算処理をして,その経過時間をプログレスバーに表示させる」というプログラムを作るとして,
一つのスレッドで,
////計算のプログラム/////////////////
計算



プログレスバーに描画
/////////////////////////////////////

とするのと,マルチスレッドにして
///メイン関数//////////////////
AfxBeginThread(・・・・) //ワーカースレッドを作成



プログレスバーを描画する
///////////////////////////////////
//ワーカースレッド(計算のプログラム)/////
計算



メインに戻す
///////////////////////////////////////

では,結果は同じですけどワーカースレッドで計算中に,
一度メインのスレッドに戻ることで長時間CPUを占有させないよう
にしているということでしょうか?

何度もお聞きしてすみません.もし,検討違いでしたら申し訳
ないです..

お礼日時:2008/06/20 18:30

・その1


適当なタイミング(10000ピクセル処理ごと、とか、プログレスバーのプロパティ変更時、など)でPumpMessageを呼ぶ。
http://msdn.microsoft.com/ja-jp/library/t1tkd768(VS.80).aspx

・その2
変換ルーチンを別スレッドにして、プログレスバーがあるメインスレッドに進捗度をメッセージで送る

ともかく「フォームやコントロールを管理しているメインスレッドで、時間のかかる事を処理し続けない」こと。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます!!
計算処理プログラム内で
MSG msg;
while (PeekMessage(&msg,0,0,0,PM_REMOVE)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
のコードをいれることで改善出来ました.

ただ,一つ質問があるのですがマルチスレッドのプログラムに挑戦して
いて次のようなプログラムを作りました.
/////CSampleDlg.h///////////////////////////
class CSampleDlg : public CDialog
{
public:
static UINT ThreadFunc( LPVOID pParam );
void MyThread();
void Keisan();

       ・
       ・
protected
CWinThread* m_pThread;
      ・
       ・
}
////CSampleDlg.cpp//////////////////////
BOOL CSampleDlg::OnInitDialog
{

        ・

m_pThread = AfxBeginThread( ThreadFunc, this );
Keisan();
}
UINT CSampleDlg::ThreadFunc( LPVOID pParam )
{
((CThreadDemoDlg*)pParam)->MyThread();
return 0;
}
void CSampleDlg::MyThread()
{
AfxMessageBox("Hello!!");
}
void Keisan()
{
for(int i=0;i<10;i++){
Sleep(100);
}
}
このようにプログラムを作成すれば,自分の想像ではAfxBeginThreadからスレッドが分かれ,Keisan()とMyThread()が同時に処理されると思ったのですが,デバックしてみるとKeisan()の処理後にMyThread()が処理されています.デバック上でそう見えるだけだと思ったのですが,実際に実行してみても何秒かたってから,Helloと表示されました.
マルチスレッドの根本の考え方がおかしいのかも知れませんが,もしよろしければなにかアドバイスを頂ければ今後の参考になります.

お礼日時:2008/06/20 09:44

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

このQ&Aと関連する良く見られている質問

Qスレッド処理からダイアログを表示するには?

Windows XPとVC++ 6.0で
時間のかかる大量の計算をするプログラムを作っています。

計算部分はスレッド処理にして
進捗ダイアログ(CDialogにCProgressCtrlを貼り付けたもの)
を表示します。

・CWinAppのInitInstanceで計算処理開始(AfxBeginThread)
・計算処理内で進捗ダイアログをCreate
・計算の進み具合によって進捗ダイアログのプログレスバーを更新

上記の流れではうまくいっていたのですが
以下のように変更したところ、進捗ダイアログをCreateするところで
プログラムが応答なしになってしまうようになりました。

・メインダイアログ(モーダル)
・メインダイアログのAボタンクリックでダイアログAを開く(モーダル)
・ダイアログAの実行ボタンクリックで計算処理開始(AfxBeginThread)
・計算処理内で進捗ダイアログをCreate
・計算の進み具合によって進捗ダイアログのプログレスバーを更新

MFC Wizardでダイアログベースで作成、MFCの共有DLLを使用しています。

モーダルダイアログ→スレッド→CDialog.Createに
制限があったりするのでしょうか。
どうかご教授ください。

Windows XPとVC++ 6.0で
時間のかかる大量の計算をするプログラムを作っています。

計算部分はスレッド処理にして
進捗ダイアログ(CDialogにCProgressCtrlを貼り付けたもの)
を表示します。

・CWinAppのInitInstanceで計算処理開始(AfxBeginThread)
・計算処理内で進捗ダイアログをCreate
・計算の進み具合によって進捗ダイアログのプログレスバーを更新

上記の流れではうまくいっていたのですが
以下のように変更したところ、進捗ダイアログをCreateするところで
プログラムが応答なしになっ...続きを読む

Aベストアンサー

スレッドを作成した側の待機処理ループの中にメッセージポンプを作成してやればよさそうですよ

pThread->ResumeThread();
do {
  AfxGetApp()->PumpMessage();
} while( WaitForSingleObject( pThread->m_hThread, 0 ) != WAIT_OBJECT_0 );
といった具合で …

メインスレッド寝てしまっているので反応無しになるのかも

Q【MFC】sleep関数を用いたモーダルダイアログを閉じる処理の実装方法

開発環境はVC++2005のMFCです。

Ctest dlg;
dlg.DoModal();

にて呼出したダイアログを、sleep関数で一定の時間経過後に
自動で閉じる処理を実装したいのですが、どのように実装すれば良いでしょうか?
またsleep関数でなく、他の時間を計る関数を用いたものでも良いので、
もしご存知の方おられましたら、ご教授お願い致します。

Aベストアンサー

Sleep()による実装はUIとしてはお勧めできません。反応が鈍くなるからです。
No.1さんのようにタイマーで代替するわけですが、ダイアログ側に実装したほうが、
使い勝手が良くなると思います。
(面倒なことはクラスで隠ぺいしちゃいましょ。それがC++流)

// ダイアログ用ヘッダ

// CTestDialog ダイアログ

class CTestDialog : public CDialog
{
DECLARE_DYNAMIC(CTestDialog)

public:
CTestDialog(CWnd* pParent = NULL); // 標準コンストラクタ
virtual ~CTestDialog();

// ダイアログ データ
enum { IDD = IDD_TEST };

protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV サポート

// タイマー用識別子(Windowsによって返された値)
UINT m_TimerID;

DECLARE_MESSAGE_MAP()
public:
virtual BOOL OnInitDialog();
afx_msg void OnTimer(UINT_PTR nIDEvent);
};

// ダイアログ 実装(cpp)

// TestDialog.cpp : 実装ファイル
//

#include "stdafx.h"
#include "test.h"
#include "TestDialog.h"


// CTestDialog ダイアログ

IMPLEMENT_DYNAMIC(CTestDialog, CDialog)

CTestDialog::CTestDialog(CWnd* pParent /*=NULL*/)
: CDialog(CTestDialog::IDD, pParent)
{

}

CTestDialog::~CTestDialog()
{
}

void CTestDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}


BEGIN_MESSAGE_MAP(CTestDialog, CDialog)
ON_WM_TIMER()
END_MESSAGE_MAP()


// CTestDialog メッセージ ハンドラ

BOOL CTestDialog::OnInitDialog()
{
CDialog::OnInitDialog();

// タイマーを設定します。
// タイマーIDの希望値を1とします
// ここでは5秒後に通知させるものとします。
// ダイアログを表示させておきたい時間に合わせて調整してください。
m_TimerID = SetTimer(1, 5*1000, NULL);

return TRUE; // return TRUE unless you set the focus to a control
// 例外 : OCX プロパティ ページは必ず FALSE を返します。
}

void CTestDialog::OnTimer(UINT_PTR nIDEvent)
{
// タイマーメッセージ応答関数です
// 本ダイアログではタイマーを一つしか使っていないので
// IDの確認はなくても動作します。
// 複数のタイマーを使用できます。
// どのタイマーかはnIDEventで区別します。

if (nIDEvent == m_TimerID)
{
// ダイアログを消します
// EndDialog()のパラメータがそのままDoModal()の戻り値となります。
// 本プログラムではDoModal()の戻り値を利用していないので
// なんでもかまいません。
// 一応、OKを押した場合と区別可能なようにIDCANCELとしました。
// 無論IDOKでも支障ありません。

EndDialog(IDCANCEL);
}

CDialog::OnTimer(nIDEvent);
}

Sleep()による実装はUIとしてはお勧めできません。反応が鈍くなるからです。
No.1さんのようにタイマーで代替するわけですが、ダイアログ側に実装したほうが、
使い勝手が良くなると思います。
(面倒なことはクラスで隠ぺいしちゃいましょ。それがC++流)

// ダイアログ用ヘッダ

// CTestDialog ダイアログ

class CTestDialog : public CDialog
{
DECLARE_DYNAMIC(CTestDialog)

public:
CTestDialog(CWnd* pParent = NULL); // 標準コンストラクタ
virtual ~CTestDialog();

// ダイア...続きを読む


人気Q&Aランキング