C++20 から標準ライブラリにセマフォが追加され、「並行処理でセマフォを使う」という選択肢が、ようやく正式に利用できるようになりました。
一方で、ミューテックスや condition_variable に比べると、セマフォは誤解されやすく、誤用も多い同期機構です。
この記事では、C++標準仕様に基づいてセマフォの役割と性質を整理し、特に混乱しやすいポイントや注意点を中心に解説します。
セマフォとは何か?
セマフォとは、簡単に言えば「同時に利用できるリソース数をカウンタで管理する同期機構」です。
ミューテックスが「常に1つのスレッドだけが通れる排他制御」を目的としているのに対し、セマフォは「最大で n 個まで同時に通過できる」ことを前提としています。
この違いにより、セマフォは以下のような用途に適しています。
- 同時実行数の制限
- リソースプールの管理
- 生産者・消費者モデル
- 完了通知やイベント待ち
セマフォの基本的な動作原理
セマフォは内部に「カウンタ」を持っています。
- 利用要求が発生すると、カウンタは減少する
- 解放が行われると、カウンタは増加する
カウンタがゼロのときに利用要求が行われた場合、そのスレッドは待機状態になります。
その後、解放が行われることで、待機していたスレッドのいずれかが再開されます。
この動作自体は非常にシンプルですが、実装や設計を誤ると深刻なバグを生むのがセマフォの難しさでもあります。
カウンティングセマフォと「最大値」の誤解
C++のカウンティングセマフォには「最大値」が存在します。
ただし、この最大値は直感的に理解されがちな「この値以上には絶対ならない」という意味ではありません。
正確には、「少なくともこの値までは扱えることが保証されている」という下限の指定です。
つまり、
- 実装ごとに内部的な最大値は異なる
- セマフォのカウンタは無限に増えるわけではない
- 上限を超えると、標準として動作は保証されない
という設計になっています。
releaseしすぎは「バグ」では済まない
セマフォで最も危険なのが、解放操作の回数が設計と合わなくなるケースです。
よくある誤解として、「releaseしすぎても、単に数が増えるだけ」と思われがちですが、これは正しくありません。
- 内部カウンタが実装の上限を超える
- 標準仕様上、動作は未定義
- クラッシュ、予測不能な挙動、データ破壊が起き得る
つまり、releaseしすぎは論理バグではなく、未定義動作に直結する重大な問題です。
この点は、ミューテックスよりもセマフォが「扱いにくい」と言われる理由の一つでもあります。
バイナリセマフォとミューテックスは同じではない
バイナリセマフォは、カウンタが 0 または 1 しか取らないセマフォです。
見た目だけを見ると、ミューテックスと同じように使えそうに見えます。
しかし、両者には決定的な違いがあります。
- ミューテックスには「所有権」がある
- セマフォには「所有権」がない
この違いにより、セマフォでは
- 取得したスレッドとは別のスレッドが解放できる
- 解放回数の不整合を検出しにくい
という性質を持ちます。
バイナリセマフォを排他制御に使うべきか?
バイナリセマフォを使って排他制御を行うこと自体は可能です。
しかし、設計上の安全性という観点では、ミューテックスに明確に劣ります。
特にチーム開発では、
- 意図しない解放
- コードレビューで気づきにくい設計ミス
- 将来的な保守性の低下
といった問題が起きやすくなります。
そのため、共有データの保護はミューテックス、通知やリソース管理はセマフォという使い分けが、最も無難で安全な設計方針です。
condition_variable との使い分け
condition_variable は柔軟性が高く、複雑な条件制御が可能です。その一方で、状態管理を自分で設計しなければなりません。
セマフォはカウンタという状態を内部に持つため、
- 単純な待ち・通知
- 同時実行数の制御
といった用途では、コードも設計もシンプルになります。
「複雑な条件 → condition_variable」「単純な数や通知 → semaphore」という整理は、実務でもよく使われる判断基準です。
まとめ:セマフォを安全に使うために
C++のセマフォは強力ですが、その分だけ注意点も多い同期機構です。
押さえておくべきポイントは以下の通りです。
- セマフォはリソース数を管理する仕組み
- 内部カウンタには最大値がある
- releaseしすぎは未定義動作につながる
- バイナリセマフォはミューテックスの代替ではない
- 排他制御と通知・リソース管理は目的で使い分ける
これらを理解していれば、セマフォは「危険な道具」ではなく、並行設計をシンプルにしてくれる有力な選択肢になります。
以上、C++のセマフォについてでした。
最後までお読みいただき、ありがとうございました。
