6. ミューテックス (同期オブジェクト)
ミューテックス (Mutex) は同期オブジェクトの一つです。「相互排他 (mutual exclusion)」の略語であり造語です。
6.1. 同期オブジェクト
同期オブジェクトとは、同じプロセス (または異なるプロセス) のスレッド間でシステムリソースのアクセスを制御する機構の事です。
待機中のスレッドは同期オブジェクトが非シグナル状態 (ビジー) の状態では処理を続行できません。同期オブジェクトがシグナル状態になると処理を続行できるようになります。
プロセス間の同期オブジェクトはシステム同期オブジェクト
、同一プロセスでの同期オブジェクトはローカル同期オブジェクト
という事になります。
同期オブジェクトに "Light-weight~" と言う名前が付いていたら、それはローカル同期オブジェクト (マルチスレッド用途) だと思って間違いありません。
6.2. TMutex
TMutex
クラス 1 を使うと、一つのスレッドからしかアクセスできない機構を作る事ができます。TMutex
は System.SyncObjs
で定義されています。
最初の書式のコンストラクタでは無名ミューテックスを作成します。無名ミューテックスはローカルミューテックスで、Critical Section と同等の動作になります。ミューテックスの作成に失敗すると EOSError
が発生します。
二番目の書式のコンストラクタでは、名前付きのミューテックスを作成します (名前が空でない場合はシステムミューテックス)。ミューテックスの作成に失敗すると EOSError
が発生します。
三番目の書式のコンストラクタでは名前付きのミューテックスを開きます。ミューテックスが開けなかった場合には EOSError
が発生します。Windows 環境でない場合には名前付きのミューテックスを作成します。
constructor Create(UseCOMWait: Boolean = False);
constructor Create(MutexAttributes: PSecurityAttributes; InitialOwner: Boolean; const Name: string; UseCOMWait: Boolean = False);
constructor Create(DesiredAccess: Cardinal; InheritHandle: Boolean; const Name: string; UseCOMWait: Boolean = False);
最初期のこのクラスは Windows API をラップしたものでした。
Delphi | Windows API |
---|---|
TMutex.Create() コンストラクタ | CreateMutexA() / CreateMutexW() |
TMutex.Create() コンストラクタ | OpenMutexW() |
TMutex.Release() メソッド | ReleaseMutex() |
Critical Section としての TMutex
の使い方は次のようになります。
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.SyncObjs {追加};
type
TMyThread = class(TThread)
private
{ Private 宣言 }
protected
procedure Execute; override;
end;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
procedure MyThreadTerminate(Sender: TObject);
{ Private 宣言 }
public
{ Public 宣言 }
end;
const
NUM_OF_THREAD = 100;
var
Form1: TForm1;
Counter: Integer;
TermCount: Integer;
Mutex: TMutex; // 追加
implementation
{$R *.dfm}
procedure TForm1.MyThreadTerminate(Sender: TObject);
begin
Inc(TermCount); // 終了したスレッドをカウント
if TermCount = NUM_OF_THREAD then // すべてのスレッドが終了したら
begin
ShowMessage(Counter.ToString); // Counter の値をダイアログで表示
Button1.Enabled := True;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Button1.Enabled := False;
Counter := 0;
TermCount := 0;
for var i := 1 to NUM_OF_THREAD do
with TMyThread.Create(True) do
begin
FreeOnTerminate := True;
OnTerminate := MyThreadTerminate;
Start;
end;
end;
{ TMyThread }
procedure TMyThread.Execute;
begin
inherited;
Mutex.Acquire;
try
var v := Counter;
Inc(v);
Mutex.WaitFor(20); // Sleep() の代わりに
Counter := v;
finally
Mutex.Release;
end;
end;
initialization
Mutex := TMutex.Create;
finalization
Mutex.Free;
end.
Acquire()
メソッドでミューテックスが解放されるまで待って、排他ロックを取得します。Release()
メソッドでミューテックスを解放します。Sleep()
の代わりに WaitFor()
を使う事もできます。
Critical Section としては、TCriticalSection
に比べると相当遅いです。
See also:
- TMutex (DocWiki)
- System.SyncObjs.TMutex.Create (DocWiki)
- System.SyncObjs.TMutex.Acquire (DocWiki)
- System.SyncObjs.TMutex.Release (DocWiki)
- System.SyncObjs.THandleObject.WaitFor (DocWiki)
6.2.1. TMutex を使ったアプリケーションの二重起動防止
ちょっとマルチスレッドからは離れますが、プロセス間同期オブジェクト (システムミューテックス) としての使い方です。
以前からミューテックスを使った二重起動防止ロジックはありましたが、TMutex
を使って次のように書くことができます。
program Project1;
uses
Vcl.Forms,
System.SysUtils,
System.SyncObjs,
WinAPI.Windows,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
const
MUTEX_NAME = 'Global\MyApp';
begin
Application.Initialize;
var MutexExists: Boolean := True;
var oMutex: TMutex;
try
oMutex := TMutex.Create(MUTEX_ALL_ACCESS, False, MUTEX_NAME);
except
on E: EOSError do
MutexExists := False;
end;
if MutexExists then
begin
Application.MessageBox('二重起動だよ!', 'エラー', MB_ICONERROR or MB_OK);
end
else
begin
var cMutex := TMutex.Create(nil, False, MUTEX_NAME);
try
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
finally
cMutex.Free;
end;
end;
FreeAndNil(oMutex);
end.
素の Windows API を使った方が簡単な気もします。
See also:
#参考
索引
[ ← 5. サードパーティ製ライブラリ ] [ ↑ 目次へ ] [ → 7. セマフォ (同期オブジェクト) ]
-
TMutex
は Delphi 2005 以降で利用可能です。 ↩