 今回はミューテックスについてやります。ミューテックスは
一度に一つのスレッドのみが所有できます。
ミューテックスの作成はCreateMutex関数で行います。
不要になったミューテックスはCloseHandleで閉じます。
クリティカルセクションと似ていますが、このミューテックスは
他のプロセスからも使用できます。
今回も、あまりよいサンプルではありません。
二つのスレッドが親ウィンドウにランダムに書き込みを
行います。スレッド1は四角形を、スレッド2は楕円を
描画します。各スレッドはWaitForSingleObject関数で
待機しています。四角形を描画するスレッドが動いているときは
他のスレッドは眠っています。描画が終わったら
ReleaseMutex関数でミューテックスの所有を解放します。
今回はミューテックスについてやります。ミューテックスは
一度に一つのスレッドのみが所有できます。
ミューテックスの作成はCreateMutex関数で行います。
不要になったミューテックスはCloseHandleで閉じます。
クリティカルセクションと似ていますが、このミューテックスは
他のプロセスからも使用できます。
今回も、あまりよいサンプルではありません。
二つのスレッドが親ウィンドウにランダムに書き込みを
行います。スレッド1は四角形を、スレッド2は楕円を
描画します。各スレッドはWaitForSingleObject関数で
待機しています。四角形を描画するスレッドが動いているときは
他のスレッドは眠っています。描画が終わったら
ReleaseMutex関数でミューテックスの所有を解放します。
実際サンプルを作ってわかったことですが、ミューテックスを 使わないとやたら四角形が描画されて楕円はたまーーーにしか 描画されません。楕円は内部の計算が大変で時間がかかるのでしょう。 ミューテックスで排他制御するとほぼ、交互に描画されるようです。 つまり、時間のかかる楕円の描画中も四角形のスレッドは 全く眠っているからだと考えられます。 (このプログラムにミューテックスを使う意味があるか??という 疑問には目をつむって、ミューテックスの使い方だけを理解してください)
この関数によりミューテックスオブジェクトを作ります。戻り値が ミューテックスハンドルとなります。HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // セキュリティ BOOL bInitialOwner, // イニシャルオーナー LPCTSTR lpName // ミューテックスオブジェクトの名前 );
lpMutexAttributesは、Windows95では無視されます。
bInitialOwnerは、この関数を呼び出したスレッドにミューテックス オブジェクトを所有させるかどうかを指定します。TRUEならば所有します。
lpNameは、オブジェクトの名前を指定します。他のプロセス(アプリケーション) から、この名前を使ってミューテックスを所有させることができます。 (ハンドルは他のプロセスでは当然使えない)
ミューテックスの所有を解放します。成功するとTRUE、失敗するとFALSEを返します。BOOL ReleaseMutex( HANDLE hMutex //ミューテックスのハンドル );
では、サンプルを見てみましょう。
ま、このへんは変わり映えしません。自作DATA型のメンバにhmutexが 加わった程度です。// mult06.cpp #define STRICT #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); DWORD WINAPI Thread1(LPVOID); DWORD WINAPI Thread2(LPVOID); char szClassName[] = "mult06"; //ウィンドウクラス typedef struct{ HWND hwnd; BOOL th_end; HANDLE hmutex; } DATA, *PDATA; int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
ここもいつもと同じです。//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるマルチスレッド",//タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
親ウィンドウができたらすぐに2つのスレッドとミューテックスを作っています。 プログラムの終了通知が来たらdata.th_end = TRUEを実行してスレッドの終了を WaitForSingleObjectで待ちます。2つともスレッドが終了したらミューテックスを クローズしてプログラムを終了します。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static HANDLE hThread1, hThread2; DWORD threadID1, threadID2; static DATA data; static HANDLE hMutex; switch (msg) { case WM_CREATE: data.hwnd = hWnd; data.th_end = FALSE; data.hmutex = hMutex; hThread1 = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)Thread1, (LPVOID)&data, 0, (LPDWORD)&threadID1); hThread2 = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)Thread2, (LPVOID)&data, 0, (LPDWORD)&threadID2); hMutex = CreateMutex(NULL, FALSE, NULL); break; case WM_CLOSE: id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { data.th_end = TRUE; WaitForSingleObject(hThread1, INFINITE); MessageBox(hWnd, "スレッド1終了", "終了", MB_OK); if (CloseHandle(hThread1) != 0) MessageBox(hWnd, "hThread1クローズ成功", "成功", MB_OK); WaitForSingleObject(hThread2, INFINITE); MessageBox(hWnd, "スレッド2終了", "終了", MB_OK); if (CloseHandle(hThread2) != 0) MessageBox(hWnd, "hThread2クローズ成功", "成功", MB_OK); if (CloseHandle(hMutex) != 0) MessageBox(hWnd, "hMutexクローズ成功", "成功", MB_OK); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }
四角形を描画するスレッドです。1つ描画したらReleaseMutex関数で 所有を解放します。ところで、一般的にCのランタイムライブラリ中の malloc(), free(), やstdio.h, io.h,などに含まれる関数を 使うときはCreateThread関数を使わないのが無難であると言われています。 またこのサンプルのようにrand()を使う場合も良くないと言われています。 (このような場合は_beginthreadまたは、_beginthreadex関数を使う) もし、何か不都合が起こればCreateThreadを使うのをやめる必要があります。 幸い、このサンプルでは特別難しいことをしていないのでこれといった 不都合は起きていないようですが、_beginthread関数で書き直してみてください。DWORD WINAPI Thread1(LPVOID pparam) { PDATA pdata; HDC hdc; RECT rc; HBRUSH hBrush; int x1, x2, y1, y2, dx, dy, r, g, b; pdata = (PDATA)pparam; while (!pdata->th_end) { WaitForSingleObject(pdata->hmutex, INFINITE); GetClientRect(pdata->hwnd, &rc); dx = rc.right - rc.left; if (dx <= 0) dx = 1; dy = rc.bottom - rc.top; if (dy <= 0) dy = 1; x1 = rand() % dx; x2 = rand() % dx; y1 = rand() % dy; y2 = rand() % dy; r = rand() % 256; g = rand() % 256; b = rand() % 256; hdc = GetDC(pdata->hwnd); hBrush = CreateSolidBrush(RGB(r, g, b)); SelectObject(hdc, hBrush); Rectangle(hdc, x1, y1, x2, y2); ReleaseDC(pdata->hwnd, hdc); DeleteObject(hBrush); ReleaseMutex(pdata->hmutex); } return 0L; }
さて、今回のサンプルはあんまり意味のないものでした。 次章では、もう少し実用に近づいたものを作ってみようと思います。DWORD WINAPI Thread2(LPVOID pparam) { PDATA pdata; HDC hdc; RECT rc; HBRUSH hBrush; int x1, x2, y1, y2, dx, dy, r, g, b; pdata = (PDATA)pparam; while (!pdata->th_end) { WaitForSingleObject(pdata->hmutex, INFINITE); GetClientRect(pdata->hwnd, &rc); dx = rc.right - rc.left; if (dx <= 0) dx = 1; dy = rc.bottom - rc.top; if (dy <= 0) dy = 1; x1 = rand() % dx; x2 = rand() % dx; y1 = rand() % dy; y2 = rand() % dy; r = rand() % 256; g = rand() % 256; b = rand() % 256; hdc = GetDC(pdata->hwnd); hBrush = CreateSolidBrush(RGB(r, g, b)); SelectObject(hdc, hBrush); Ellipse(hdc, x1, y1, x2, y2); ReleaseDC(pdata->hwnd, hdc); DeleteObject(hBrush); ReleaseMutex(pdata->hmutex); } return 0L; }
Update Dec/07/1997 By Y.Kumei