# <6> ミューテックス (同期オブジェクト) (Delphi コンカレントプログラミング) --- tags: Delphi programming Pascal embarcadero objectpascal created_at: 2021-12-05 updated_at: 2021-12-13 --- # 6. ミューテックス (同期オブジェクト) **ミューテックス (Mutex)** は同期オブジェクトの一つです。「相互排他 (**mut**ual **ex**clusion)」の略語であり造語です。 ## 6.1. 同期オブジェクト 同期オブジェクトとは、同じプロセス (または異なるプロセス) のスレッド間でシステムリソースのアクセスを制御する機構の事です。 待機中のスレッドは同期オブジェクトが**非シグナル状態** (ビジー) の状態では処理を続行できません。同期オブジェクトが**シグナル状態**になると処理を続行できるようになります。 プロセス間の同期オブジェクトは`システム同期オブジェクト`、同一プロセスでの同期オブジェクトは`ローカル同期オブジェクト`という事になります。 同期オブジェクトに **"Light-weight~"** と言う名前が付いていたら、それはローカル同期オブジェクト (マルチスレッド用途) だと思って間違いありません。 ## 6.2. TMutex `TMutex` クラス [^1] を使うと、一つのスレッドからしかアクセスできない機構を作る事ができます。`TMutex` は `System.SyncObjs` で定義されています。 最初の書式のコンストラクタでは無名ミューテックスを作成します。無名ミューテックスはローカルミューテックスで、Critical Section と同等の動作になります。ミューテックスの作成に失敗すると `EOSError` が発生します。 二番目の書式のコンストラクタでは、名前付きのミューテックスを作成します (名前が空でない場合はシステムミューテックス)。ミューテックスの作成に失敗すると `EOSError` が発生します。 三番目の書式のコンストラクタでは名前付きのミューテックスを開きます。ミューテックスが開けなかった場合には `EOSError` が発生します。Windows 環境でない場合には名前付きのミューテックスを作成します。 ```pascal 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()](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TMutex.Create) コンストラクタ | [CreateMutexA()](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexa) / [CreateMutexW()](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexw)| | [TMutex.Create()](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TMutex.Create) コンストラクタ | [OpenMutexW()](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-openmutexw)| | [TMutex.Release()](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TMutex.Release) メソッド | [ReleaseMutex()](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-releasemutex) | Critical Section としての `TMutex` の使い方は次のようになります。 ```pascal 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)](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TMutex) - [System.SyncObjs.TMutex.Create (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TMutex.Create) - [System.SyncObjs.TMutex.Acquire (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TMutex.Acquire) - [System.SyncObjs.TMutex.Release (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TMutex.Release) - [System.SyncObjs.THandleObject.WaitFor (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.THandleObject.WaitFor) ## 6.2.1. TMutex を使ったアプリケーションの二重起動防止 ちょっとマルチスレッドからは離れますが、プロセス間同期オブジェクト (システムミューテックス) としての使い方です。 以前からミューテックスを使った二重起動防止ロジックはありましたが、`TMutex` を使って次のように書くことができます。 ```pascal:Project1.dpr 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:** - [二重起動を防止 - FDELPHI Delphi FAQ (delfusa.main.jp)](http://delfusa.main.jp/delfusafloor/archive/www.nifty.ne.jp_forum_fdelphi/faq/00022.htm) - [[Delphi] アプリケーションの多重起動を防止する (Win32) (くろねこ研究所)](https://www.blackcat.xyz/article.php/ProgramingFAQ_del0008) #参考 - [ミューテックス (Wikipedia)](https://ja.wikipedia.org/wiki/%E3%83%9F%E3%83%A5%E3%83%BC%E3%83%86%E3%83%83%E3%82%AF%E3%82%B9) # 索引 [ [← 5. サードパーティ製ライブラリ](./e7f655bb73af05fbac35.md) ] [ [↑ 目次へ](https://qiita.com/items/e8c1ff3a4c74e4c2a4f3) ] [ [→ 7. セマフォ (同期オブジェクト)](./6d7aa5ab5856d231b79e.md) ] [^1]: `TMutex` は Delphi 2005 以降で利用可能です。