最終更新日時(UTC):
が更新

履歴 編集

class template
<semaphore>

std::counting_semaphore(C++20)

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

処理系

関連項目

参照