3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

一度だけ初期化してほしいときは関数スコープの static 変数を使ったり

Last updated at Posted at 2021-04-21

これは何?

一度だけ実行したい初期化処理があるときにどうするのがよいか。
C++11 以降で。

まずい例

擬似コード
if ( 初期化処理やるべき? ){ // [A]
  初期化処理やるべき? = false; // [B]
  初期化();
  初期化済み? = true
}
while( ! 初期化済み? ){ちょっと寝る();}

みたいに書きたくなるけど、これは複数スレッドから呼ばれると不幸になることがある。
一番乗りが [A] に到達したあと、 [B] が実行される前に他のスレッドが [A] を通り抜けてしまうかもしれない。

で。どうするか。

案1. atomic_exchange

[A] と [B] をまとめてアトミックに実施できればよいので

擬似コード
if ( std::atomic_exchange(&初期化処理やるべき?, false) ){ // [A] + [B]
  初期化();
  初期化済み? = true
}
while( ! 初期化済み? ){ちょっと寝る();}

とすればうまくいくと思う。
変数 初期化済み?volatile かつ atomic にすることをお忘れなく。

案2. call_once

とはいえ「一度だけ呼ぶ」という趣旨の関数がすでにあるのでそれを使ったほうが簡単。

擬似コード
static std::once_flag once;
std::call_once(once, 初期化);

この記事を書くまで知らなかったんだけど、複数スレッドから立て続けに std::call_once 呼ぶと、一番乗りの std::call_once が終わるまで、二番手以降の std::call_once はブロックされるらしい。便利。

案3. static 変数

static 変数は、C++11 以降であれば複数スレッドからでもちゃんと一回だけ初期化されるので、こんな風にしてもいい。

というか、こう書けるように型を定義するのが無理ない感じなら、これが一番平易だと思う。

擬似コード
struct some_type { some_type(){ 初期化処理(); } };
static some_type some_obj;

まとめ

というわけで

  • 一度だけ初期化したい
  • 複数のスレッドが初期化コードのあたりに到達するかもしれない
  • もちろん未初期化で初期化コードより先に行かれると困る

ということなら、 static 変数か、 std::call_once がおすすめだよ。

3
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?