FAQ12-1

Q. Win3.1用プロセス停止プログラムをWin95でも動作させるには

 GetTickCount()を利用して,一定時間待機する処理を作ろうとしています.
 そこで,リスト12-1のようなコードを作成しました.このコードは,10秒間,自分のプロセスだけを停止するものです.
 このコードを,32ビットアプリケーションとして,Windows95で実行してみるとうまくいきません.Windows3.1時代に,16ビットアプリケーションとして使っていたときは正常に動作していたのですが...
 実際に動かないことはないのですが,10秒間停止している間,CPUの使用率が100%になってしまい,他のプロセスの動きが遅くなってしまうのです.
 Windows3.1時代は,他のプロセスが遅くなるようなことはありませんでした.どうすればよいのでしょうか.お願いします.

[リスト12-1] :

DWORD	dwTime = GetTickCount();

while( GetTickCount() - dwTime < 10000 ) {
	MSG	msg;
	PeekMessage( &msg, NULL, NULL, NULL, PM_NOREMOVE );
}


A. Sleep()とスレッドを利用する

PeekMessage()は必要ない

 まず,32ビットアプリケーションでは,PeekMessage()を呼ぶ必要はありません.
 メッセージを処理するならば,意味がないとは言えませんが,他のプロセスに制御を明け渡すためだけに呼んでいる場合は,ほとんど意味がありません.リスト12-2のように書いても効果は同じです.
 しかし,これではCPUの使用率が100%であることには変わりありません.
 そこで,32ビットアプリケーションではSleep()という新しいAPIを使います.

[リスト12-2] :

DWORD	dwTime = GetTickCount();

while( GetTickCount() - dwTime < 10000 ) {
	MSG	msg;
	if( PeekMessage( &msg, hwnd, WM_MYMESSAGE,
			WM_MYMESSAGE, PM_NOREMOVE ) ) {
		break;
	}
}

Sleep()

 Sleep()とは,現在のプロセス(正確にはスレッド)を指定された時間だけ停止させるAPIです.指定する時間は,ミリ秒単位で,
 Sleep( 10000 );
というように使用します.
 これを使用すれば,CPUを無駄に使用することなく,待機だけをすることができます.

あるアクションがあるまで待機するには,マルチスレッドを利用する

 あるアクション,すなわち何かメッセージが送られてきたり,コールバック関数が呼ばれた場合に,Sleep()を中断することはできないのでしょうか.
 残念ながらそれはできません.では,何かメッセージが来るまでSleep()するためには,質問にあったようにPeekMessage()ループを使う方法しかないのでしょうか.
 しかし,これではまたCPUの使用率が,100%になってしまい,振り出しに戻ってしまいます.確かにループ中にSleep(100)とかを入れることによって,多少は改善されるかもしれませんが,それでは完璧ではありません.
 そんな困ったときには,マルチスレッドを使います(リスト12-3).
 リスト12-3では,CreateThread()でWaitingThread()というスレッドを作っています.WaitingThread()はSleep()をするだけのためのスレッドです.
 メインのコードは,GetMessage()でメッセージが来るのを待機します.GetMessage()は,PeekMessage()とは違い,メッセージが来るまで制御を戻さないので,いく分か効率がよくなります.
 GetMessage()した後,DispatchMessage()していないので,前のサンプルとは少し動作が変わってきますが,そこらへんは臨機応変に対応しましょう.

[リスト12-3] :

#define WM_MYMESSAGE (WM_USER + 0)

void MainFunction( void ) {
	HANDLE	hThread;
	DWORD	idThread;
	hThread = CreateThread( NULL, 8192,
			(LPTHREAD_START_ROUTINE)WaitingThread,
			NULL, 0, &idThread );

	MSG	msg;
	while( GetMessage( &msg, NULL, NULL, NULL ) ) {
		if( msg.message == WM_MYMESSAGE ) {
			break;
		}
		//TranslateMessage( &msg );
		//DispatchMessage( &msg );
	}

	TerminateThread( hThread, 0 );
	CloseHandle( hThread );
}

DWORD WaitingThread( LPVOID ) {
	Sleep( 10000 );
	PostMessage( hwnd, WM_MYMESSAGE, 0, 0 );
	Sleep( INFINITE );
	return 0;
}
 メッセージというアクションだけでなく,コールバック関数が呼ばれたときや,その他のアクションの場合はPostMessage()に変換すればよいでしょう(リスト12-4).

[リスト12-4] :

void CallBackFunction( void ) {
	PostMessage( hwnd, WM_MYMESSAGE, 0, 0 );
}

Back to FAQ main page