Version 14.05
ミューテックスを使おう!
「というわけで、今回から同期オブジェクトの具体的な使用方法について説
明します」
『はーい』
「まずは、一番使われている【ミューテックス ( Mutex )】から」
『なんで一番よく使われてるの?』
「フリーウェアとかで〈何度実行してもプロセスがひとつだけしか起動しな
い〉っていうアプリあるでしょ」
『うん、よくあるね。あ、それに使われてるってこと?』
「そういうこと。だから普通の同期オブジェクトとしての使い方で言えば、
必ずしもよく使われてるってことでもないかな」
『ふぅ〜ん』
「さて、では実際にミューテックスを使ってみます。まず、 VC で新しくテ
ストプロジェクトを作って。プロジェクト名は ProcessA で」
『ダイアログのでいいの?』
「その方がいいかな。ちなみにテスト用プロジェクトの作り方については
Version 4.01 ( No.051 ) を参照」
『うん、できたよ』
「じゃ、次にボタンを5つ貼り付けて、ボタンを5つ貼り付けて、それぞれ
のイベントハンドラを作って」
『む、5つって結構めんどい……ん、作ったよ』
「さて、ここからがプログラム。その5つのイベントハンドラの直前に次の
ようにプログラムを書いてください」
namespace B1
{
HANDLE g_hMutex = NULL;
const char *const MUTEX_NAME = "ProcessesMutex";
// ミューテックスを作成します。
void Create()
{
g_hMutex = CreateMutex( NULL, TRUE, MUTEX_NAME );
TRACE( "%x\n", g_hMutex );
}
// ミューテックスを取得します。
void Open()
{
g_hMutex = OpenMutex( MUTEX_ALL_ACCESS, FALSE, MUTEX_NAME );
TRACE( "%x\n", g_hMutex );
}
// ミューテックスを監視して待機状態に入ります。
void Wait()
{
TRACE( "待機開始。\n" );
DWORD dwResult = WaitForSingleObject( g_hMutex, 120000 );
if( dwResult == WAIT_TIMEOUT )
{
TRACE( "タイムアウト。\n" );
}
else
{
TRACE( "待機終了。\n" );
}
}
// ミューテックスを手放します。
void Release()
{
ReleaseMutex( g_hMutex );
}
// ミューテックスを削除します。
void Close()
{
CloseHandle( g_hMutex );
}
}
『お、結構長い』
「で、それぞれのイベントハンドラの中から、それぞれひとつずつ関数を呼
んでください。たとえば、ボタンの ID を書き換えずにイベントハンドラを
作った場合、関数名は OnButton1() ってなるから……」
void CProcessADlg::OnButton1()
{
B1::Create();
}
「という感じに呼び出すようにしてください」
『関数が5つだからボタンも5つ、ね』
「さて、ではひとつずつ見ていきます。まずは変数から」
HANDLE g_hMutex = NULL;
「これはミューテックスのハンドルを入れるためのグローバル変数」
『 HANDLE 、って API だとファイルとかもこれだよね』
「そう、 CreateFile() の戻り値、ファイルを管理するためのハンドルもこ
の HANDLE でした。ファイルも、同期オブジェクトも、全部 HANDLE で操作
することになります」
『む、なんかそれって危険じゃない? 型でハンドルの区別付かないんだ』
「そうなんだよね。 Win32API はこういう所も含めて危険だから……」
『うー』
「次に、ミューテックスの名前」
const char *const MUTEX_NAME = "ProcessesMutex";
「同期オブジェクトは、基本的に〈名前〉で識別します」
『名前、前回言ってた〈テスト〉とかのこと?』
「そうそれ。ウィンドウズ全体で共通で使うものだから、こういう名前を
使って識別します」
『他のプログラムとかち合っちゃわない?』
「その可能性もある……けど、明確なルールはないかな。1プロセスだけな
らアプリのフルパスを使えばいいんだけど」
『それなら問題なしね』
「今回のテストは複数のプロセスでアクセスするからそうもいかなくてね。
実際には他とかち合わないようなもっと長い文字列にしましょう」
『む、なんかテキトー』
「さて、ここからプログラム。まずはミューテックスの作成と削除から」
// ミューテックスを作成します。
void Create()
{
g_hMutex = CreateMutex( NULL, TRUE, MUTEX_NAME );
TRACE( "%x\n", g_hMutex );
}
// ミューテックスを削除します。
void Close()
{
CloseHandle( g_hMutex );
}
『ってゆーかファイルに似てる……』
「ま、同じハンドルを扱う API だからね。 CreateMutex() がミューテック
スを作る API 。第1引数はセキュリティ関係の面倒な部分なので NULL で
OK。第2引数の TRUE は、作った直後にすぐ所有するか」
『? 作ったらすぐ持てるんじゃないの?』
「持たないこともできるってこと。前回説明したでしょ、ミューテックスを
持つとシグナルがオフで、手放すとオンになるって」
『あ! そっか、それと関係してるんだ』
「そういうこと。この辺は次回に回します。第3引数はさっき説明した
ミューテックスの名前」
『これで決められるわけね……』
「さて、次はミューテックスの取得と解放」
// ミューテックスを取得します。
void Open()
{
g_hMutex = OpenMutex( MUTEX_ALL_ACCESS, FALSE, MUTEX_NAME );
TRACE( "%x\n", g_hMutex );
}
// ミューテックスを手放します。
void Release()
{
ReleaseMutex( g_hMutex );
}
「これが、今言った、シグナルに関係する部分」
『 OpenMutex() を呼ぶとミューテックスを取得してシグナルがオフ、
ReleaseMutex() を呼ぶと手放してシグナルオン、ってこと?』
「そういうこと。これも具体的な使用例は次回説明します」
『でもさ、作ったときに持つことできるんでしょ? だったら意味ないん
じゃ……』
「他のプロセスがもう既に作ってたら?」
『あ、作る意味ない……』
「そういう場合には OpenMutex() で開くだけだから」
『なるほど』
「最後に、待つための関数」
// ミューテックスを監視して待機状態に入ります。
void Wait()
{
TRACE( "待機開始。\n" );
DWORD dwResult = WaitForSingleObject( g_hMutex, 120000 );
if( dwResult == WAIT_TIMEOUT )
{
TRACE( "タイムアウト。\n" );
}
else
{
TRACE( "待機終了。\n" );
}
}
「この WaitForSingleObject() は、開いたミューテックスを監視します」
『これが前回言ってた〈待機状態〉になるってやつね』
「そういうこと。 WaitForSingleObject() は、第1引数で渡された
〈同期オブジェクトのハンドル〉のシグナルがオンになるまで待ちます」
『つまり WaitForSingleObject() を呼んだらそのまま返ってこないってこ
と?』
「そういうこと。ただし、第2引数の時間が経ったら、シグナルがオンにな
らなくても待つのをやめて WAIT_TIMEOUT を返してきます」
『ん? なんかすごい大きな数字なんだけど……』
「単位はミリ秒」
『あー、120秒=2分?』
「そういうこと。ま、これはプログラムによりけりだから。タイムアウトさ
せたくない場合は INFINITE を指定します」
『でもなんか危険そう……』
「というわけで次回に続く!」
/*
Preview Next Story!
*/
『むむむ、今回だけじゃ全然わかんない!』
「というわけで次回は実際に使ってみます」
『うんうん、具体例ないとわかんないよね』
「というわけで次回」
< Version 14.06 ミューテックスで排他処理 >
『につづく!』
「これで今回のコードの意味がわかると思うよ」
『ってゆーかそっち先の方が良かったなー』
「使い方だけわかっても意味ないでしょ」
『う”』