Skip to content

Latest commit

 

History

History
132 lines (102 loc) · 6.5 KB

File metadata and controls

132 lines (102 loc) · 6.5 KB

atomic

  • memory[meta header]
  • std[meta namespace]
  • class template[meta id-type]
  • cpp20[meta cpp]
namespace std {
  template <class T> struct atomic; // 先行宣言

  template <class T> struct atomic<shared_ptr<T>>;
  template <class T> struct atomic<weak_ptr<T>>;
}
  • shared_ptr[link shared_ptr.md]
  • weak_ptr[link weak_ptr.md]

概要

<memory>ヘッダでは、std::shared_ptrstd::weak_ptrクラスに対するstd::atomicクラスの特殊化を定義する。

これらの特殊化を使用することで、共通のスマートポインタオブジェクトに対する複数スレッドでの操作をアトミックに行える。

定数初期化

C++23でconstexpr atomic(nullptr_t) noexceptコンストラクタが追加され、非アトミックなshared_ptrweak_ptrと同様に、constinitを使ってnullptrから定数初期化できるようになった:

// グローバル変数を、動的初期化ではなく定数初期化する
constinit std::atomic<std::shared_ptr<int>> g_resource{nullptr};

非アトミックなshared_ptrではconstinit std::shared_ptr<int> p{nullptr};が可能であったが、C++20時点のatomic<shared_ptr<T>>nullptrを受け取るコンストラクタ経路(nullptrから一時オブジェクトのshared_ptrを構築し、atomic(shared_ptr<T>)コンストラクタに渡す)がconstexprでなかったため、constinitによる定数初期化ができなかった。この非対称性が解消された。

constinitによる定数初期化には以下の利点がある:

  • 静的初期化順序問題 (static initialization order fiasco) の回避 : グローバル・名前空間スコープのオブジェクトを動的初期化すると、ほかの翻訳単位のグローバル変数の初期化との実行順序が未規定となり、初期化前のオブジェクトにアクセスしてしまう危険がある。定数初期化されるオブジェクトはプログラム開始前に初期化が完了しているため、どの動的初期化よりも先に初期化済みであることが保証され、この問題を避けられる
  • 動的初期化のオーバーヘッド排除 : 実行時に初期化コードが走らない

共通メンバ関数

名前 説明 対応バージョン
(constructor) コンストラクタ C++20
constexpr atomic(nullptr_t) noexcept nullptrから構築する C++23
~atomic() = default デストラクタ C++20
operator= 代入演算子 C++20
is_lock_free オブジェクトがロックフリーに振る舞えるかを判定する C++20
store 値を書き込む C++20
load 値を読み込む C++20
operator T 型Tへの変換演算子 C++20
exchange 値を入れ替える C++20
compare_exchange_weak 弱い比較で値を入れ替える C++20
compare_exchange_strong 強い比較で値を入れ替える C++20
wait 起床されるまで待機する C++20
notify_one 待機しているスレッドをひとつ起床させる C++20
notify_all 待機している全てのスレッドを起床させる C++20

共通メンバ型

名前 説明 対応バージョン
value_type 要素型となるテンプレートパラメータの型T
shared_ptrに対する特殊化ではshared_ptr<T>となる。
weak_ptrに対する特殊化ではweak_ptr<T>となる
C++20

共通メンバ定数

名前 説明 対応バージョン
static constexpr bool is_always_lock_free Tに対するアトミック操作が常にロックフリー (非ミューテックス) で動作する場合はtrue、そうでなければfalse C++20

#include <iostream>
#include <memory>
#include <thread>

std::atomic<std::shared_ptr<int>> resource;

// Producer-Consumerパターン。
// 供給者スレッドがデータを作成し、消費者スレッドが供給されたデータを使用する
void producer() {
  std::shared_ptr<int> x{new int(3)};
  resource.store(x);
}

void consumer() {
  // データが供給されたら、resourceとyを入れ替える (resourceが空になり、yにデータが入る)。
  std::shared_ptr<int> y;
  while (!(y = resource.exchange(y))) {
  }
  std::cout << *y << std::endl;
}

int main()
{
  std::thread consumer_thread{consumer};
  std::thread producer_thread{producer};

  consumer_thread.join();
  producer_thread.join();
}
  • std::atomic<std::shared_ptr>[color ff0000]
  • resource.store[link /reference/atomic/atomic/store.md]
  • resource.exchange[link /reference/atomic/atomic/exchange.md]
  • consumer_thread.join()[link /reference/thread/thread/join.md]
  • producer_thread.join()[link /reference/thread/thread/join.md]

出力

3

バージョン

言語

  • C++20

処理系

参照