名前空間
変種
操作

std::call_once

提供: cppreference.com
< cpp‎ | thread
 
 
スレッドサポートライブラリ
スレッド
(C++11)
(C++20)
(C++20)
this_thread 名前空間
(C++11)
(C++11)
(C++11)
相互排他
(C++11)
汎用ロック管理
(C++11)
(C++11)
(C++11)
(C++11)(C++11)(C++11)
(C++11)
call_once
(C++11)
条件変数
(C++11)
セマフォ
ラッチとバリア
(C++20)
(C++20)
フューチャー
(C++11)
(C++11)
(C++11)
(C++11)
 
ヘッダ <mutex> で定義
template< class Callable, class... Args >
void call_once( std::once_flag& flag, Callable&& f, Args&&... args );
(C++11以上)

複数のスレッドから並行的に呼ばれた場合でも、 Callable なオブジェクト f をちょうど一度だけ実行します。

詳細は以下の通りです。

  • call_once が呼ばれた時点で、 f がすでに呼ばれたことを flag が示す場合、 call_once はすぐに戻ります (このような call_once の呼び出しは passive と言います)。
  • そうでなければ、 call_once は引数 std​::​forward<Args>(args)... を用いて ​std​::​forward<Callable>(f) を呼びます。 std::thread のコンストラクタや std::async と異なり、引数は別のスレッドに転送される必要はないため、ムーブもコピーもされません (このような call_once の呼び出しは active と言います)。
  • その呼び出しが例外を投げた場合、それは call_once の呼び出し元に伝搬され、別の呼び出しを試みられるようにフラグの反転は行われません (このような call_once の呼び出しは exceptional と言います)。
  • その呼び出しが正常に戻った場合 (このような call_once の呼び出しは returning と言います)、フラグは反転され、同じフラグを使用した他のすべての call_once の呼び出しは passive となることが保証されます。

同じ flag に対するすべての active な呼び出しは、0回以上の exceptional な呼び出しに続く1回の returning な呼び出しから構成される単一の全順序を形成します。 その順序に��いて、それぞれの active な呼び出しの終わりは、次の active な呼び出しに対して同期します。

returning な呼び出しからの戻りは、同じ flag に対するすべての passive な呼び出しからの戻りに対して同期します。 これは、すべての並行的な call_once の呼び出しが、 active な呼び出しによってなされたあらゆる副作用を、追加の同期なしに観測することが保証されることを意味します。

目次

[編集] 引数

flag - ちょうど一度だけ関数を実行するためのオブジェクト
f - 実行する Callable なオブジェクト
args... - 関数に渡す引数

[編集] 戻り値

(なし)

[編集] 例外

  • 何らかの状況により call_once の呼び出しが指定された内容を実行することが妨げられる場合、 std::system_error
  • f によって投げられるあらゆる例外。

[編集] ノート

call_once の並行的な呼び出しに異なる f を渡した場合、どの f が呼ばれるかは未規定です。 選択された関数は、それが渡された call_once の呼び出しと同じスレッドで実行されます。

関数ローカルな static 変数の初期化は、複数のスレッドから呼ばれた場合でも一度だけ行われることが保証されており、 std::call_once を使用した同等なコードよりも効率が良い場合があります。

この関数のPOSIX版は pthread_once です。

[編集]

#include <iostream>
#include <thread>
#include <mutex>
 
std::once_flag flag1, flag2;
 
void simple_do_once()
{
    std::call_once(flag1, [](){ std::cout << "Simple example: called once\n"; });
}
 
void may_throw_function(bool do_throw)
{
  if (do_throw) {
    std::cout << "throw: call_once will retry\n"; // これは2回以上表示されるかもしれません。
    throw std::exception();
  }
  std::cout << "Didn't throw, call_once will not attempt again\n"; // 1回だけが保証されます。
}
 
void do_once(bool do_throw)
{
  try {
    std::call_once(flag2, may_throw_function, do_throw);
  }
  catch (...) {
  }
}
 
int main()
{
    std::thread st1(simple_do_once);
    std::thread st2(simple_do_once);
    std::thread st3(simple_do_once);
    std::thread st4(simple_do_once);
    st1.join();
    st2.join();
    st3.join();
    st4.join();
 
    std::thread t1(do_once, true);
    std::thread t2(do_once, true);
    std::thread t3(do_once, false);
    std::thread t4(do_once, true);
    t1.join();
    t2.join();
    t3.join();
    t4.join();
}

出力例:

Simple example: called once
throw: call_once will retry
throw: call_once will retry
Didn't throw, call_once will not attempt again

[編集] 欠陥報告

以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。

DR 適用先 発行時の動作 正しい動作
LWG 2442 C++11 the arguments are copied and/or moved before invocation no copying/moving is performed

[編集] 関連項目

(C++11)
call_once が一度だけ関数を呼び出すことを保証するためのヘルパーオブジェクト
(クラス) [edit]