<7> セマフォ (同期オブジェクト) (Delphi コンカレントプログラミング)

# 7. セマフォ (同期オブジェクト)

**セマフォ (Semaphore)** は同期オブジェクトの一つです [^1]。所定の数のスレッドのアクセスを許可します。

スレッドカウンタの最大数が 1 のセマフォを**バイナリセマフォ**と呼び、ミューテックスと同等となります。

## 7.1. TSemaphore

`TSemaphore` クラス [^2] を使うと、指定された数のスレッドからしかアクセスできない機構を作る事ができます。`TSemaphore` は `System.SyncObjs` で定義されています。

最初の書式のコンストラクタではカウンタの初期値が 1 の無名セマフォを作成します。無名セマフォはローカルセマフォで、Critical Section と同等の動作になります。よって、このコンストラクタを使うとミューテックスと同等の動作になります。セマフォの作成に失敗すると `EOSError` が発生します。

二番目の書式のコンストラクタでは、名前付きのセマフォを作成します (名前が空でない場合はシステムセマフォ)。セマフォの作成に失敗すると `EOSError` が発生します。

三番目の書式のコンストラクタでは名前付きのセマフォを開きます。セマフォが開けなかった場合には `EOSError` が発生します。Windows 環境でない場合には名前付きのセマフォを作成します。

```pascal
constructor Create(UseCOMWait: Boolean = False);
constructor Create(SemaphoreAttributes: PSecurityAttributes; AInitialCount, AMaximumCount: Integer; const Name: string; UseCOMWait: Boolean = False);
constructor Create(DesiredAccess: Cardinal; InheritHandle: Boolean; const Name: string; UseCOMWait: Boolean = False);
```

最初期のこのクラスは Windows API をラップしたものでした。

| Delphi | Windows API |
|:---|:---|
| [TSemaphore.Create()](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TSemaphore.Create) コンストラクタ | [CreateSemaphoreW()](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createsemaphorew)|
| [TSemaphore.Create()](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TSemaphore.Create) コンストラクタ | [OpenSemaphoreW()](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-opensemaphorew)|
| [TSemaphore.Release()](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TSemaphore.Release) メソッド | [ReleaseSemaphore()](https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-releasesemaphore) |

使い方は `TMutex` とほぼ同じです。

**See also:**

 - [TSemaphore (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TSemaphore)
 - [System.SyncObjs.TSemaphore.Create (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TSemaphore.Create)
 - [System.SyncObjs.TSemaphore.Acquire (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TSemaphore.Acquire)
 - [System.SyncObjs.TSemaphore.Release (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TSemaphore.Release)
 - [System.SyncObjs.THandleObject.WaitFor (DocWiki)](http://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.THandleObject.WaitFor)

### 7.1.1. TSemaphore を使った起動数の制御
ちょっとマルチスレッドからは離れますが、プロセス間同期オブジェクト (システムセマフォ) としての使い方です。

`TMutex` のような要領で、指定された数しか開けないアプリケーションを作る方法です。

```pascal
program Project1;

uses
  Vcl.Forms,
  System.SysUtils,
  System.SyncObjs,
  WinAPI.Windows,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

const
  SEMAPHORE_NAME = 'Global\MyApp';

begin
  ReportMemoryLeaksOnShutdown := True;
  Application.Initialize;
  var oSemaphore, cSemaphore: TSemaphore;
  try
    oSemaphore := TSemaphore.Create(SEMAPHORE_ALL_ACCESS, False, SEMAPHORE_NAME);
  except
    on E: EOSError do
      cSemaphore := TSemaphore.Create(nil, 2, 3, SEMAPHORE_NAME);
  end;
  if Assigned(oSemaphore) and (oSemaphore.WaitFor(0) <> wrSignaled) then
    begin
      Application.MessageBox('３つまでしか開けないよ！', 'エラー', MB_ICONERROR or MB_OK);
    end
  else
    begin
      Application.MainFormOnTaskbar := True;
      Application.CreateForm(TForm1, Form1);
      Application.Run;
      if Assigned(oSemaphore) then
        oSemaphore.Release;
    end;
  FreeAndNil(cSemaphore);
  FreeAndNil(oSemaphore);
end.
```

素の Windows API を使った方が簡単な気もします。

## 7.2. TLightweightSemaphore
`TLightweightSemaphore` クラス [^3] は軽量なセマフォです。`System.SyncObjs` で定義されています。但し、ローカルセマフォとしてしか使えません。

.NET の `SemaphoreSlim` クラスに相当します。

**See also:**

 - [TLightweightSemaphore (DocWiki)](https://docwiki.embarcadero.com/Libraries/ja/System.SyncObjs.TLightweightSemaphore)
 - [SemaphoreSlim クラス (docs.microsoft.com)](https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.semaphoreslim)


#参考

 - [セマフォ (Wikipedia)](https://ja.wikipedia.org/wiki/%E3%82%BB%E3%83%9E%E3%83%95%E3%82%A9)
 - [Using Semaphores in Delphi, Part 1 (EDN)](https://edn.embarcadero.com/article/29908)
 - [Using Semaphores in Delphi, Part 2 (EDN)](https://edn.embarcadero.com/article/30027)

# 索引
[ [← 6. ミューテックス (同期オブジェクト)](https://qiita.com/ht_deko/items/83cd0ab35c90a9a6cea1) ] [ [↑ 目次へ](https://qiita.com/items/e8c1ff3a4c74e4c2a4f3) ] [ [→ <8> イベント (同期オブジェクト) ](https://qiita.com/ht_deko/items/c6317b1bb7ad6dd47476) ]


[^1]: 語源となったのは`腕木式信号機`です。僕がこの用語を初めて知ったのは『SX‐WINDOWプログラミング』だったと思います。
[^2]: `TSemaphore` は Delphi 2009 以降で利用可能です。
[^3]: `TLightweightSemaphore` は Delphi XE 以降で利用可能です。え？`TLightweightMutex` が存在しない理由ですか？