最終更新日時: 2024年06月11日 22時45分38秒 (JST)
Akira Takahashi が更新

履歴 編集

class
<latch>

# std::latch(C++20)

    namespace std {
      class latch;
    }

## 概要

`latch`クラスは、ラッチ通過/到達スレッドが期待する個数になるまで複数スレッドの進行をブロックする、スレッド調整機構（同期プリミティブ）である。

ラッチは1つのカウンタで状態管理される同期プリミティブとみなせる。カウンタは`0`以上かつ初期値以下の値をとり、このカウンタ値がラッチ未到達のスレッド個数と解釈される。 その動作仕様からカウントダウン・ラッチと呼ばれることもある。

- コンストラクタにてカウンタ値を設定する。
- `count_down()`によりカウンタ値の減算と通知を行う。（ラッチ通過）
- `wait()`によりカウンタ値が`0`になるまで呼び出しスレッドをブロックする。（ラッチ待機）
- `arrive_and_wait()`は`count_down()`＋`wait()`に相当する。（ラッチ到達 兼 待機）

ラッチオブジェクトは1回だけ使用でき、カウンタ値が`0`に到達したオブジェクトの再利用はできない。 複数スレッド間同期を繰り返しとる場合はバリア`barrier`を利用する。

ラッチ`latch`はカウンタ値`0`を待機するが、類似機構のセマフォ`counting_semaphore`はカウンタ値が`0`より大きいことを待機するという違いがある。

## メンバ関数

名前 | 説明 | 対応バージョン
--- | --- | ---
`(constructor)` | コンストラクタ | C++20
`(destructor)` | デストラクタ | C++20
`operator=(const latch&) = delete;` | 代入演算子 | C++20
`count_down` | 通過通知 | C++20
`try_wait` | カウンタ値が`0`か否かを確認 | C++20
`wait` | 待機処理 | C++20
`arrive_and_wait` | 到達通知と待機処理 | C++20

## 静的メンバ関数

名前 | 説明 | 対応バージョン
--- | --- | ---
`max` | 処理系でサポートされるカウンタ最大値 | C++20

## 例

    #include <iostream>
    #include <latch>
    #include <mutex>
    #include <thread>

    constexpr int NWORKER = 3;  // ワーカ数

    std::mutex cout_mtx;  // 行単位cout出力用ミューテックス

    int main()
    {
      // 1回だけ初期化される共有データ
      int shared_data = 0;

      // 初期化済みを表すラッチ: 初期カウント値=1
      std::latch initialized{1};
      // タスク完了を表すラッチ: 初期カウント値=ワーカ数+1(メインスレッド)
      std::latch completed{NWORKER+1};

      // ワーカスレッド群をFire-and-Forget起動
      for (int id = 1; id <= NWORKER; id++) {
        std::thread([&,id]{
          // 共有データの初期化完了を待機
          initialized.wait();
          // メインスレッド上でのshared_data代入完了は保証されており、
          // 以降はshared_data読出のみであればデータ競合発生しない。
          int local_data = shared_data;

          { // ワーカスレッドのタスクを実行
            std::lock_guard lk{cout_mtx};
            std::cout << "Worker#" << id << ":" << local_data << std::endl;
          }

          // タスク完了を通知
          completed.count_down();
          // ワーカスレッドはブロックされずそのまま終了する
        }).detach();
      }

      // 共有データを初期化
      shared_data = 42;

      // 共有データ初期化完了をワーカスレッド群へ通知
      initialized.count_down();
      // メインスレッドはブロックされず後続行を実行する

      { // メインスレッドのタスクを実行
        std::lock_guard lk{cout_mtx};
        std::cout << "Main:" << shared_data << std::endl;
      }

      // メインスレッドのタスク完了通知 兼 全ワーカスレッドのタスク完了待機
      completed.arrive_and_wait();
    }

### 出力例

    Worker#1:42
    Worker#2:42
    Main:42
    Worker#3:42

## バージョン

### 言語

- C++20

### 処理系

- Clang: 11.0 ✅
- GCC: ??
- ICC: ??
- Visual C++: ??

## 関連項目

- `<semaphore>`
- `<barrier>`

## 参照

- Boost.Threadライブラリ, Latches
- Java標準ライブラリ, java.util.concurrent.CountDownLatch
- C#標準ライブラリ, System.Threading.CountdownEvent