namespace std {
template<ptrdiff_t least_max_value = implementation-defined>
class counting_semaphore;
using binary_semaphore = counting_semaphore<1>;
}
概要
counting_semaphore
クラスは、スレッド間で使用する共有リソースへの並行アクセスを制約する、軽量な同期プリミティブである。
カウンティングセマフォ(または単にセマフォ)は他の同期プリミティブを実装するための部品として広く用いられ、セマフォと条件変数のいずれも適用可能なケースではcondition_variable
よりも効率的である。
セマフォは1つのカウンタで状態管理される同期プリミティブとみなせる。カウンタは0
以上かつmax()
以下の値をとり、このカウンタ値がセマフォで管理する共有リソースの個数と解釈される。
- カウンタの最大値はテンプレートパラメータ
least_max_value
にて設定する。省略時は処理系がサポートする最大値が設定される。 - カウンタの初期値はコンストラクタでセマフォ構築時に指定する。
release()
メンバ関数呼び出しによりカウンタ値の加算と通知処理、つまり利用可能な共有リソースの増加を表現する。(歴史的にはオランダ語由来の"V操作"と呼ばれる。英語では"up"/"signal"/"post"とも呼ばれる。)acquire()
メンバ関数呼び出しにより待機処理とカウンタ値の減算、つまり利用可能な共有リソースの減少を表現する。(同様に"P操作"や"down"/"wait"/"pend"とも呼ばれる。)
カウンティングセマフォの最大値を1
としたものはバイナリセマフォと呼ばれ、ヘッダ<semaphore>
ではエイリアス型binary_semaphore
として定義される。
バイナリセマフォはミューテックス(mutex
など)と同様に共有リソースの排他制御を実現する同期プリミティブだが、ミューテックスとは異なりスレッドがロックを所有(own)するという概念が存在しない。
このためバイナリセマフォでは、あるスレッド上でカウンタ値を減少(1→0; lock
相当)させ、別のスレッド上でカウンタ値を増加(0→1; unlock
相当)させてもよい。
適格要件
テンプレートパラメータleast_max_value
には非負の数値を指定する。
メンバ関数
構築・破棄
名前 | 説明 | 対応バージョン |
---|---|---|
(constructor) |
コンストラクタ | C++20 |
(destructor) |
デストラクタ | C++20 |
operator=(const counting_semaphore&) = delete; |
代入演算子 | C++20 |
release |
カウンタ値を加算し、待機中スレッドをブロック解除する | C++20 |
acquire |
カウンタ値が0 より大きくなるまで待機し、カウンタ値を1つ減算する |
C++20 |
try_acquire |
カウンタ値の1減算を試みる | C++20 |
try_acquire_for |
相対時間のタイムアウトを指定して、カウンタ値の1減算を試みる | C++20 |
try_acquire_until |
絶対時間のタイムアウトを指定して、カウンタ値の1減算を試みる | C++20 |
静的メンバ関数
名前 | 説明 | 対応バージョン |
---|---|---|
max |
カウンタの最大値 | C++20 |
例
#include <iostream>
#include <semaphore>
#include <thread>
#include <queue>
int main()
{
// アイテム在庫
std::queue<int> stock;
// 在庫管理カウンティングセマフォ(初期値=0/最大値=3)
std::counting_semaphore<3> token{0};
// 在庫アクセス保護バイナリセマフォ(初期値=1/最大値=1)
std::counting_semaphore<1> guard{1};
// 説明のためテンプレートパラメータleast_max_valueを明示指定しているが、
// 両者ともstd::counting_semaphore<>と省略しても正しく動作する。
// 生産者スレッド
std::thread producer([&]{
for (int i = 1; i <= 3; i++) {
// 新しいアイテムを生産
int item = i * 100;
// アイテム在庫へ追加
guard.acquire();
stock.push(item);
guard.release();
// 在庫管理カウントを+1する
token.release();
}
});
// 消費者スレッド
std::thread consumer([&]{
for (int i = 1; i <= 3; i++) {
// 在庫生産されるまで待機し、在庫管理カウントを-1する
token.acquire();
// アイテム在庫から取出
guard.acquire();
int item = stock.front();
stock.pop();
guard.release();
// アイテムを消費
std::cout << item << std::endl;
}
});
producer.join();
consumer.join();
return 0;
}
出力
100
200
300
バージョン
言語
- C++20
処理系
- Clang: 11.0 ✅
- GCC: ??
- ICC: ??
- Visual C++: ??