スレッドの待機


スリープ

前回のサンプルで体験した様に、スレッドはタイマーと異なり
CPU をより有効利用して、並行処理を CPU の空き時間をフルに使って行います

これは、時間のかかる処理を主スレッドに行わせることを避ける方法として使えますが
他にも、アニメーション処理などに活躍させることができるでしょう
ところが、アニメーションを指せようとすると思わぬ障害にあたります

スレッドを無限ループのアニメーションで実現しようとすると
あまりにも処理が速すぎて、アニメーション速度を制御できないのです
しかし、無意味な計算をさせて遅くすることは資源の無駄使いである
マルチタスク OS の環境下では、「公共の福祉」を考えなければならない
あなたのプログラムが無駄な計算をして速度を調整するくらいなら
その分、有用な計算のために CPU を貸してほしいと思っているソフトウェアがあるかもしれない

では、どうやってスレッドの速度などを調整するべきなのでしょう?
実は、スレッドは Sleep() 関数で一時待機させることができます

VOID Sleep(DWORD dwMilliseconds);

dwMelliseconds には、ミリ秒単位で待機時間を設定します
関数を呼び出したスレッドは、指定した時間だけ待機し、その後、実行可能状態に戻ります
引数に 0 を指定すれば、残りのタイムスライスを同じ優先順位の他のスレッドに譲ります
タイムスライスとは、スレッドが持つ CPU を使うことができる時間のことです

また、INFINITE を引数に指定すると、永遠に待機します
#include <windows.h>

DWORD WINAPI ThreadFunc(LPVOID hWnd) {
	HDC hdc;
	RECT rect;
	int iX;

	for (iX = 0 ; TRUE ; iX++) {
		hdc = GetDC(hWnd);
		GetClientRect(hWnd , &rect);

		SelectObject(hdc , GetStockObject(BLACK_BRUSH));
		Rectangle(hdc , 0 ,  0 , iX , 20);

		ReleaseDC(hWnd , hdc);
		if (iX >= rect.right) {
			InvalidateRect(hWnd , NULL , TRUE);
			iX = 0;
		}
		Sleep(10);
	}
}

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	DWORD dwParam;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		CreateThread(NULL , 0 , ThreadFunc , hWnd , 0 , &dwParam);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}
プログラムを実行すると、黒い横棒がウィンドウの左から右に伸びていきます
棒はウィンドウの右端にたどり着くと、再び左から右に伸びていきます

このアニメーションは、スレッドによって実現されています
スレッドのループの最後で Sleep() 関数を使ってスレッドを待機させています
スレッドは、指定した時間が経過すると再び動き出します

これを用いれば、高度なグラフィックアニメーションを実現することができるでしょう
ビットマップなどを使った高度なアニメーションに、ぜひ挑戦してみてください


サスペンド

Sleep() 関数で用いるスレッドの待機は良く使われる一般的な方法です
アニメーション関連のプログラムを得意とする人は、よく使うでしょう

このスレッド待機は時間による待機でした
プログラマやユーザの意思に関係なく、時間が立てば勝手に動き出します
しかし、実行を許可するまでスレッドを動かしたくない場合もあるでしょう

このような場合はサスペンドカウンタを使います
サスペンドカウンタは、スレッドオブジェクトが持つ待機と実行を制御するカウンタで
カウンタが 0 の時は実行し、そうでなければ待機する仕掛けになっています

サスペンドカウンタをインクリメントするには SuspendThread() 関数を
サスペンドカウンタをデクリメントするには ResumeThread() 関数を使います

DWORD SuspendThread(HANDLE hThread);
DWORD ResumeThread(HANDLE hThread);

hThread には対象となるスレッドのハンドルを指定します
成功すれば以前のサスペンドカウントが、失敗すると 0xFFFFFFFF が返ります
#include <windows.h>

DWORD WINAPI ThreadFunc(LPVOID hWnd) {
	HDC hdc;
	RECT rect;
	int iX;

	for (iX = 0 ; TRUE ; iX++) {
		hdc = GetDC(hWnd);
		GetClientRect(hWnd , &rect);

		SelectObject(hdc , GetStockObject(BLACK_BRUSH));
		Rectangle(hdc , 0 ,  0 , iX , 20);

		ReleaseDC(hWnd , hdc);
		if (iX >= rect.right) {
			InvalidateRect(hWnd , NULL , TRUE);
			iX = 0;
		}
		Sleep(10);
	}
}

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	DWORD dwParam;
	static HANDLE hThread;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		hThread = CreateThread(
			NULL , 0 , ThreadFunc , hWnd , 0 , &dwParam);
		return 0;
	case WM_RBUTTONDOWN:
		SetCapture(hWnd);
		SuspendThread(hThread);
		return 0;
	case WM_RBUTTONUP:
		ResumeThread(hThread);
		ReleaseCapture();
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}
これは、先ほどの黒い棒が横に伸びていくプログラムを改良したものです
マウスの右ボタンを押すとスレッドはその場で待機し、離すとスレッドは再び実行します


Sleep()

VOID Sleep(DWORD dwMilliseconds);

スレッドを指定時間だけ待機させます
引数に 0 を指定するとタイムスライスを同じ優先順位のスレッドに譲ります
そのようなスレッドが無い場合は、即座に実行を続けます
INFINITE を指定すれば、永遠にスレッドは待機します

dwMelliseconds - ミリ秒単位で待機時間を設定します

SuspendThread()

DWORD SuspendThread(HANDLE hThread);

スレッドのサスペンドカウントを 1 加算させます
サスペンドカウントが 0 以外であれば、スレッドは待機します

hThread - 対象のスレッドを指定します

戻り値 - 以前のサスペンドカウント。失敗すれば 0xFFFFFFFF

ResumeThread()

DWORD ResumeThread(HANDLE hThread);

スレッドのサスペンドカウントを 1 減算させます
サスペンドカウントが 0 であれば、スレッドは実行します

hThread - 対象のスレッドを指定します

戻り値 - 以前のサスペンドカウント。失敗すれば 0xFFFFFFFF



前のページへ戻る次のページへ