SlideShare a Scribd company logo
Effective Modern C++
勉強会#3
Item 16
刈谷 満(@kariya_mitsuru)
2015/4/22
Item 16: Make const member functions thread safe.
const メンバ関数を
スレッドセーフにしよう
Item 16: Make const member functions thread safe.
例
多項式を表すクラス
⇒Polynomial
その多項式の根を計算するメンバ関数
⇒roots
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType =
std::vector<double>;
RootsType roots() const;
…
};
多項式の根を計算するメンバ関数は、多項式自体を変更しない
↓
const メンバ関数
根を保持するデータ構造
(“using” については Item 9 を参照)
Item 16: Make const member functions thread safe.
多項式の根の計算は高くつく。
⇓
必要なければ計算したくない。
でも 2 回以上は計算したくない。
⇓
最初に使われた時に計算しよう!
計算した根はキャッシュしよう!
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
bool rootsAreValid{ false };
RootsType rootVals{};
};
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
bool rootsAreValid{ false };
RootsType rootVals{};
};
キャッシュが有効じゃなかったら…
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
bool rootsAreValid{ false };
RootsType rootVals{};
};
キャッシュが有効じゃなかったら…
根を計算してそれを rootVals に保存し…
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
bool rootsAreValid{ false };
RootsType rootVals{};
};
キャッシュが有効じゃなかったら…
根を計算してそれを rootVals に保存し…
キャッシュを有効にする!
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
bool rootsAreValid{ false };
RootsType rootVals{};
};
キャッシュが有効じゃなかったら…
根を計算してそれを rootVals に保存し…
キャッシュを有効にする!
あれ?
const メンバ関数じゃなかったっけ?
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
mutable bool rootsAreValid{ false };
mutable RootsType rootVals{};
};
mutable!
roots は概念的にはオブジェクトを変更しない(論理的 const 性)
詳しくはWebで!!
Effective C++ 第3版、Item 3 参照
Item 16: Make const member functions thread safe.
ところで、この Polynomial クラスのオブジェクトを複数スレッドで同時に使うと…
Polynomial p;
…
スレッド2
auto valsGivingZero = p.roots();
スレッド1
auto rootsOfP = p.roots();
const メンバ関数だから読むだけだし、
同期しなくても大丈夫っしょ!!
Item 16: Make const member functions thread safe.
ところで、この Polynomial クラスのオブジェクトを複数スレッドで同時に使うと…
Polynomial p;
…
スレッド2
auto valsGivingZero = p.roots();
スレッド1
auto rootsOfP = p.roots();
const メンバ関数だから読むだけだし、
同期しなくても大丈夫っしょ!!
死
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
mutable bool rootsAreValid{ false };
mutable RootsType rootVals{};
};
スレッド2スレッド1
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
mutable bool rootsAreValid{ false };
mutable RootsType rootVals{};
};
スレッド2スレッド1
①rootsAreValid を読む
⇓
まだ false
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
mutable bool rootsAreValid{ false };
mutable RootsType rootVals{};
};
スレッド2スレッド1
①rootsAreValid を読む
⇓
まだ false
②rootsAreValid を読む
⇓
まだ false!
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
mutable bool rootsAreValid{ false };
mutable RootsType rootVals{};
};
スレッド2スレッド1
①rootsAreValid を読む
⇓
まだ false
③計算しては書き込む!
(まだ false)
②rootsAreValid を読む
⇓
まだ false!
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
mutable bool rootsAreValid{ false };
mutable RootsType rootVals{};
};
スレッド2スレッド1
①rootsAreValid を読む
⇓
まだ false
③計算しては書き込む!
(まだ false)
②rootsAreValid を読む
⇓
まだ false!
④計算しては書き込む!
(まだ false)
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
mutable bool rootsAreValid{ false };
mutable RootsType rootVals{};
};
スレッド2スレッド1
①rootsAreValid を読む
⇓
まだ false
③計算しては書き込む!
(まだ false)
②rootsAreValid を読む
⇓
まだ false!
④計算しては書き込む!
(まだ false)
データレース!
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
if (!rootsAreValid) {
…
rootsAreValid = true;
}
return rootVals;
}
private:
mutable bool rootsAreValid{ false };
mutable RootsType rootVals{};
};
スレッド2スレッド1
①rootsAreValid を読む
⇓
まだ false
③計算しては書き込む!
(まだ false)
②rootsAreValid を読む
⇓
まだ false!
④計算しては書き込む!
(まだ false)
データレース!ようこそUBへ!
Item 16: Make const member functions thread safe.
ここでの問題は…
roots は const メンバ関数なのに、スレッドセーフじゃない。
⇓
const メンバ関数であることは C++11 でも C++98 でも正しい。
(rootsは多項式の値を変更しない)
⇓
直すべきはスレッドセーフ性の欠如
Item 16: Make const member functions thread safe.
解決策
mutex を使え
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
std::lock_guard<std::mutex> g(m);
ここは前と一緒…
return rootVals;
}
private:
mutable std::mutex m;
ここも前と一緒…
};
lock_guard を使って
m をロック!
ブロックの終わりで
m をアンロック!
ここの mutable にも注意!
ロック、アンロックは const メンバ関数じゃない
でも、m が変わっても多項式はやっぱり変わらない!
Item 16: Make const member functions thread safe.
Caution!(Errataあり)
std::mutex はコピーもムーブもできない。
Polynomial に m を追加した結果、
コピーもムーブも出来なくなった。
Item 16: Make const member functions thread safe.
Caution!(Errataあり)
std::mutex はコピーもムーブもできない。
Polynomial に m を追加した結果、
コピーもムーブも出来なくなった。
独自のコピー、ムーブコンストラ
クタ、代入演算子を定義すれば
いいと思うんだけど…
Item 16: Make const member functions thread safe.
コメントあり
もし Polynomial が変更不可だったら、std::call_once と
std::once_flag を使う方がいんじゃね?
⇓
ちょっと書いてみました
Item 16: Make const member functions thread safe.
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const {
std::call_once(flag, [this]{ calc(); });
return rootVals;
}
void calc() const {
…
}
private:
mutable std::once_flag flag{};
mutable RootsType rootVals{};
};
call_once を使って
calc を一回だけ呼ぶ
(λ をつかってる理由は
Item 34 参照)
実際に計算、格納するメンバ関数
もちろん const メンバ関数
もちろん once_flag も mutable!
Item 16: Make const member functions thread safe.
場合によっては mutex は重過ぎ
例)メンバ関数が呼ばれた回数を数えたい
⇓
std::atomic で十分
(実際に std::atomic の方が mutex より軽いかどうかは、
動作するハードウェアと標準ライブラリの実装によるので注意!)
Item 16: Make const member functions thread safe.
class Point {
public:
…
double distanceFromOrigin() const noexcept
{
++callCount;
return std::hypot(x, y);
}
private:
mutable std::atomic<unsigned> callCount{ 0 };
double x, y;
};
アトミック(不可分)なインクリメント
(Item 40 参照)
Item 16: Make const member functions thread safe.
class Point {
public:
…
double distanceFromOrigin() const noexcept
{
++callCount;
return std::hypot(x, y);
}
private:
mutable std::atomic<unsigned> callCount{ 0 };
double x, y;
};
例によって、std::atomic<T> も
コピーはおろかムーブも
出来ないので注意!
(本書Errata)
Item 16: Make const member functions thread safe.
class Point { // 二次元の点
public:
…
double distanceFromOrigin() const noexcept // noexcept については
{
++callCount;
return std::hypot(x, y);
}
private:
mutable std::atomic<unsigned> callCount{ 0 };
double x, y;
};
あ、あと、std::hypotも
わりとデキる奴なので
忘れないであげてください!(><)
(本書Errata)
hypotenuse squared
Item 16: Make const member functions thread safe.
でも…
mutex より std::atomic の方が軽いからと言って、
使いすぎは禁物!
例)重い計算結果のキャッシュ(int)と
キャッシュの有効フラグ(bool)
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = val1 + val2;
cacheValid = true;
return cachedValue;
}
}
private:
mutable std::atomic<bool> cacheValid{ false };
mutable std::atomic<int> cachedValue;
};
キャッシュする値と
その有効フラグを
atomic にしました!
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = val1 + val2;
cacheValid = true;
return cachedValue;
}
}
private:
mutable std::atomic<bool> cacheValid{ false };
mutable std::atomic<int> cachedValue;
};
キャッシュする値と
その有効フラグを
atomic にしました!
ホントに
大丈夫…?
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = val1 + val2;
cacheValid = true;
return cachedValue;
}
}
private:
mutable std::atomic<bool> cacheValid{ false };
mutable std::atomic<int> cachedValue;
};
スレッド2スレッド1
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = val1 + val2;
cacheValid = true;
return cachedValue;
}
}
private:
mutable std::atomic<bool> cacheValid{ false };
mutable std::atomic<int> cachedValue;
};
スレッド2スレッド1
①有効か確認する
⇓
まだ無効
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = val1 + val2;
cacheValid = true;
return cachedValue;
}
}
private:
mutable std::atomic<bool> cacheValid{ false };
mutable std::atomic<int> cachedValue;
};
スレッド2スレッド1
②計算して格納
(まだ無効)
①有効か確認する
⇓
まだ無効
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = val1 + val2;
cacheValid = true;
return cachedValue;
}
}
private:
mutable std::atomic<bool> cacheValid{ false };
mutable std::atomic<int> cachedValue;
};
スレッド2スレッド1
②計算して格納
(まだ無効)
③有効か確認する
⇓
まだ無効
①有効か確認する
⇓
まだ無効
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = val1 + val2;
cacheValid = true;
return cachedValue;
}
}
private:
mutable std::atomic<bool> cacheValid{ false };
mutable std::atomic<int> cachedValue;
};
スレッド2スレッド1
②計算して格納
(まだ無効)
③有効か確認する
⇓
まだ無効
①有効か確認する
⇓
まだ無効
④計算して格納
(まだ無効)
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = val1 + val2;
cacheValid = true;
return cachedValue;
}
}
private:
mutable std::atomic<bool> cacheValid{ false };
mutable std::atomic<int> cachedValue;
};
スレッド2スレッド1
②計算して格納
(まだ無効)
③有効か確認する
⇓
まだ無効
①有効か確認する
⇓
まだ無効
④計算して格納
(まだ無効)残念!!!
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
じゃあじゃあ、
キャッシュの有効化と
値の代入を逆にしよう!
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
じゃあじゃあ、
キャッシュの有効化と
値の代入を逆にしよう!
ホントにホントに
大丈夫…?
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
スレッド2スレッド1
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
スレッド2スレッド1
①有効か確認する
⇓
まだ無効
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
スレッド2スレッド1
②2つとも計算する
(まだ無効)
①有効か確認する
⇓
まだ無効
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
スレッド2スレッド1
②2つとも計算する
(まだ無効)
③有効か確認する
⇓
まだ無効
①有効か確認する
⇓
まだ無効
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
スレッド2スレッド1
②2つとも計算する
(まだ無効)
③有効か確認する
⇓
まだ無効
①有効か確認する
⇓
まだ無効
④2つとも計算する
(まだ無効)
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
スレッド2スレッド1
②2つとも計算する
(まだ無効)
③有効か確認する
⇓
まだ無効
①有効か確認する
⇓
まだ無効
④2つとも計算する
(まだ無効)
残念!!!
(本書Errata)
Item 16: Make const member functions thread safe.
でも…
実はもっとヤバい…
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
スレッド2スレッド1
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
スレッド2スレッド1
①有効か確認する
⇓
まだ無効
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
スレッド2スレッド1
②2つとも計算する
(まだ無効)
①有効か確認する
⇓
まだ無効
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
スレッド2スレッド1
②2つとも計算する
(まだ無効)
③有効にする!
(まだ格納前!)
①有効か確認する
⇓
まだ無効
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
スレッド2スレッド1
②2つとも計算する
(まだ無効)
③有効にする!
(まだ格納前!)
④有効か確認する
⇓
有効!?
値を返す!!!
①有効か確認する
⇓
まだ無効
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cacheValid = true;
return cachedValue = val1 + val2;
}
}
…
};
スレッド2スレッド1
②2つとも計算する
(まだ無効)
③有効にする!
(まだ格納前!)
④有効か確認する
⇓
有効!?
値を返す!!!
①有効か確認する
⇓
まだ無効
死
Item 16: Make const member functions thread safe.
教訓
単一の変数やメモリ位置の同期が必要
⇒ std::atomic
2 つ以上の変数やメモリ位置に対して不可分操作が必要
⇒ mutex(std::atomic じゃNG!)
Item 16: Make const member functions thread safe.
class Widget {
public:
…
int magicValue() const {
std::lock_guard<std::mutex> guard(m);
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = val1 + val2;
cacheValid = true;
return cachedValue;
}
}
…
private:
mutable std::mutex m;
mutable int cachedValue;
mutable bool cacheValid{ false };
};
lock_guard を使って
m をロック!
ブロックの終わりで
m をアンロック!
例によって mutable!
もはや atomic の必要なし!
Item 16: Make const member functions thread safe.
ところで…
散々マルチスレッドの話をしてきたけど、
シングルスレッド前提の場合は?
⇓
これらの対応は不要
(その場合、mutex や std::atomic に
関するコストを避けられる)
Item 16: Make const member functions thread safe.
ところで…
散々マルチスレッドの話をしてきたけど、
シングルスレッド前提の場合は?
⇓
これらの対応は不要
(その場合、mutex や std::atomic に
関するコストを避けられる)
Item 16: Make const member functions thread safe.
え
ー
マ
ジ
マ
ル
チ
ス
レ
ッ
ド
未
対
応
⁉
マ
ル
チ
ス
レ
ッ
ド
未
対
応
が
許
さ
れ
る
の
は
小
学
生
ま
で
だ
よ
ね
ー
©キモイガールズ
Item 16: Make const member functions thread safe.
覚えておくこと
• 並行コンテキストで決して使用しないと確信できない限り、
const メンバ関数をスレッドセーフにしておくこと。
• std::atomic の使用は mutex よりも良いパフォーマンスを示す
かもしれないが、それは単一の変数やメモリ位置への操作のみに適
している。

More Related Content

PPTX
Effective Modern C++勉強会#4 Item 17, 18資料
Ryo Igarashi
 
PDF
Effective Modern C++ 勉強会#3 Item 15
Mitsuru Kariya
 
PPTX
Effective Modern C++ 勉強会 Item 22
Keisuke Fukuda
 
PPTX
Emcpp0506
Takatoshi Kondo
 
PDF
templateとautoの型推論
MITSUNARI Shigeo
 
PDF
Effective Modern C++ 勉強会 Item26
Akihiro Nishimura
 
PDF
中3女子でもわかる constexpr
Genya Murakami
 
PDF
Effective Modern C++ 勉強会#1 Item3,4
Takashi Hoshino
 
Effective Modern C++勉強会#4 Item 17, 18資料
Ryo Igarashi
 
Effective Modern C++ 勉強会#3 Item 15
Mitsuru Kariya
 
Effective Modern C++ 勉強会 Item 22
Keisuke Fukuda
 
Emcpp0506
Takatoshi Kondo
 
templateとautoの型推論
MITSUNARI Shigeo
 
Effective Modern C++ 勉強会 Item26
Akihiro Nishimura
 
中3女子でもわかる constexpr
Genya Murakami
 
Effective Modern C++ 勉強会#1 Item3,4
Takashi Hoshino
 

What's hot (20)

PDF
NumPyが物足りない人へのCython入門
Shiqiao Du
 
PDF
高速な倍精度指数関数expの実装
MITSUNARI Shigeo
 
PPTX
Effective Modern C++ Item 9 and 10
uchan_nos
 
PDF
組み込みでこそC++を使う10の理由
kikairoya
 
PDF
Constexpr 中3女子テクニック
Genya Murakami
 
PDF
Effective Modern C++ 勉強会#7 Item 27
Mitsuru Kariya
 
PDF
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
Genya Murakami
 
PPTX
Effective modern c++ 8
uchan_nos
 
PDF
二分探索法で作る再帰呼び出しできるCプリプロセッサマクロ
digitalghost
 
PDF
Boost.Coroutine
melpon
 
PDF
CTF for ビギナーズ ネットワーク講習資料
SECCON Beginners
 
PDF
effective modern c++ chapeter36
Tatsuki SHIMIZU
 
PPTX
競技プログラミングのためのC++入門
natrium11321
 
PDF
すごい constexpr たのしくレイトレ!
Genya Murakami
 
PDF
ctypes拡張モジュール
Moriyoshi Koizumi
 
PDF
ARM CPUにおけるSIMDを用いた高速計算入門
Fixstars Corporation
 
PDF
CRC-32
7shi
 
PPTX
C#/.NETがやっていること 第二版
信之 岩永
 
PDF
CTF for ビギナーズ バイナリ講習資料
SECCON Beginners
 
PDF
組み込み関数(intrinsic)によるSIMD入門
Norishige Fukushima
 
NumPyが物足りない人へのCython入門
Shiqiao Du
 
高速な倍精度指数関数expの実装
MITSUNARI Shigeo
 
Effective Modern C++ Item 9 and 10
uchan_nos
 
組み込みでこそC++を使う10の理由
kikairoya
 
Constexpr 中3女子テクニック
Genya Murakami
 
Effective Modern C++ 勉強会#7 Item 27
Mitsuru Kariya
 
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
Genya Murakami
 
Effective modern c++ 8
uchan_nos
 
二分探索法で作る再帰呼び出しできるCプリプロセッサマクロ
digitalghost
 
Boost.Coroutine
melpon
 
CTF for ビギナーズ ネットワーク講習資料
SECCON Beginners
 
effective modern c++ chapeter36
Tatsuki SHIMIZU
 
競技プログラミングのためのC++入門
natrium11321
 
すごい constexpr たのしくレイトレ!
Genya Murakami
 
ctypes拡張モジュール
Moriyoshi Koizumi
 
ARM CPUにおけるSIMDを用いた高速計算入門
Fixstars Corporation
 
CRC-32
7shi
 
C#/.NETがやっていること 第二版
信之 岩永
 
CTF for ビギナーズ バイナリ講習資料
SECCON Beginners
 
組み込み関数(intrinsic)によるSIMD入門
Norishige Fukushima
 
Ad

Viewers also liked (15)

PDF
Effective modern-c++#9
Tatsuki SHIMIZU
 
PDF
emc++ chapter32
Tatsuki SHIMIZU
 
PPTX
Effective modern C++ 勉強会 #3 Item 12
Keisuke Fukuda
 
PPTX
Effective Modern C++ study group Item39
Takatoshi Kondo
 
PPTX
Effective Modern C++勉強会#2 Item 11(,12)
Keisuke Fukuda
 
PDF
Effective Modern C++ 勉強会#6 Item25
Takashi Hoshino
 
PDF
Effective Modern C++ 勉強会#8 Item38
Takashi Hoshino
 
PDF
Effective Modern C++ Item 24: Distinguish universal references from rvalue re...
mooopan
 
PDF
Emcjp item33,34
MITSUNARI Shigeo
 
PDF
emcjp Item 42
MITSUNARI Shigeo
 
PDF
Emcjp item21
MITSUNARI Shigeo
 
PDF
Emcpp item31
mitsutaka_takeda
 
PDF
Emcpp item41
mitsutaka_takeda
 
PDF
Effective Modern C++ 読書会 Item 35
Keisuke Fukuda
 
PPTX
Effective modern c++ 5
uchan_nos
 
Effective modern-c++#9
Tatsuki SHIMIZU
 
emc++ chapter32
Tatsuki SHIMIZU
 
Effective modern C++ 勉強会 #3 Item 12
Keisuke Fukuda
 
Effective Modern C++ study group Item39
Takatoshi Kondo
 
Effective Modern C++勉強会#2 Item 11(,12)
Keisuke Fukuda
 
Effective Modern C++ 勉強会#6 Item25
Takashi Hoshino
 
Effective Modern C++ 勉強会#8 Item38
Takashi Hoshino
 
Effective Modern C++ Item 24: Distinguish universal references from rvalue re...
mooopan
 
Emcjp item33,34
MITSUNARI Shigeo
 
emcjp Item 42
MITSUNARI Shigeo
 
Emcjp item21
MITSUNARI Shigeo
 
Emcpp item31
mitsutaka_takeda
 
Emcpp item41
mitsutaka_takeda
 
Effective Modern C++ 読書会 Item 35
Keisuke Fukuda
 
Effective modern c++ 5
uchan_nos
 
Ad

Similar to Effective Modern C++ 勉強会#3 Item16 (20)

PDF
Hello Dark-Side C# (Part. 1)
Yuto Takei
 
PDF
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
y_taka_23
 
PDF
Boost.Flyweight
gintenlabo
 
PPTX
.NET Core 2.x 時代の C#
信之 岩永
 
PDF
第2回勉強会スライド
koturn 0;
 
PDF
Javaセキュアコーディングセミナー東京第3回講義
JPCERT Coordination Center
 
PPTX
Stream2の基本
shigeki_ohtsu
 
PDF
Unity2015_No10_~UGUI&Audio~
CHY72
 
PDF
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第6回 ‟文字列とオブジェクト„
和弘 井之上
 
PPTX
本当にあった怖い話し Db編
Oda Shinsuke
 
PDF
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
Yoshifumi Kawai
 
PDF
asm.js x emscripten: The foundation of the next level Web games
Noritada Shimizu
 
PDF
xv6から始めるSPIN入門
Ryousei Takano
 
PDF
Move semantics
mitsutaka_takeda
 
PPTX
Build Node.js-WASM/WASI Tiny compiler with Node.js
mganeko
 
PPTX
冬のLock free祭り safe
Kumazaki Hiroki
 
PDF
今日からできる!簡単 .NET 高速化 Tips
Takaaki Suzuki
 
PDF
わんくま同盟大阪勉強会#61
TATSUYA HAYAMIZU
 
PPT
Altanative macro
Motohiro KOSAKI
 
PDF
スタート低レイヤー #0
Kiwamu Okabe
 
Hello Dark-Side C# (Part. 1)
Yuto Takei
 
思ったほど怖くない! Haskell on JVM 超入門 #jjug_ccc #ccc_l8
y_taka_23
 
Boost.Flyweight
gintenlabo
 
.NET Core 2.x 時代の C#
信之 岩永
 
第2回勉強会スライド
koturn 0;
 
Javaセキュアコーディングセミナー東京第3回講義
JPCERT Coordination Center
 
Stream2の基本
shigeki_ohtsu
 
Unity2015_No10_~UGUI&Audio~
CHY72
 
【C++BUILDER STARTER チュートリアルシリーズ】シーズン2 C++Builderの部 第6回 ‟文字列とオブジェクト„
和弘 井之上
 
本当にあった怖い話し Db編
Oda Shinsuke
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
Yoshifumi Kawai
 
asm.js x emscripten: The foundation of the next level Web games
Noritada Shimizu
 
xv6から始めるSPIN入門
Ryousei Takano
 
Move semantics
mitsutaka_takeda
 
Build Node.js-WASM/WASI Tiny compiler with Node.js
mganeko
 
冬のLock free祭り safe
Kumazaki Hiroki
 
今日からできる!簡単 .NET 高速化 Tips
Takaaki Suzuki
 
わんくま同盟大阪勉強会#61
TATSUYA HAYAMIZU
 
Altanative macro
Motohiro KOSAKI
 
スタート低レイヤー #0
Kiwamu Okabe
 

Recently uploaded (11)

PDF
20250730_QiitaBash_LT登壇資料_PDC_Kurashina.pdf
pdckurashina
 
PDF
TaketoFujikawa_ComicComputing12th_inKumamoto
Matsushita Laboratory
 
PDF
LoRaWAN ウェザーステーションキット v3 -WSC3-L 日本語ユーザーマニュアル
CRI Japan, Inc.
 
PDF
第三世代 ウェザーステーションキット v3 ー WSC3-L 日本語カタログ
CRI Japan, Inc.
 
PDF
【学会聴講報告】CVPR2025からみるVision最先端トレンド / CVPR2025 report
Sony - Neural Network Libraries
 
PDF
VMUG Japan book vsan 20250515 CPU/Memory vSAN
Kazuhiro Sota
 
PPTX
2025_7_25_吉祥寺_設計ナイト_ADR運用におけるデータ利活用の考え方.pptx
ssuserfcafd1
 
PDF
20250726_Devinで変えるエンプラシステム開発の未来
Masaki Yamakawa
 
PDF
20250729_Devin-for-Enterprise
Masaki Yamakawa
 
PPTX
baserCMS『カスタムコンテンツ』徹底活用術〜あなただけの管理画面を自由自在に〜
Ryuji Egashira
 
PDF
MahiroYoshida_セリフに着目したキャラクタロール推定に関する基礎検討_sigcc12th2025
Matsushita Laboratory
 
20250730_QiitaBash_LT登壇資料_PDC_Kurashina.pdf
pdckurashina
 
TaketoFujikawa_ComicComputing12th_inKumamoto
Matsushita Laboratory
 
LoRaWAN ウェザーステーションキット v3 -WSC3-L 日本語ユーザーマニュアル
CRI Japan, Inc.
 
第三世代 ウェザーステーションキット v3 ー WSC3-L 日本語カタログ
CRI Japan, Inc.
 
【学会聴講報告】CVPR2025からみるVision最先端トレンド / CVPR2025 report
Sony - Neural Network Libraries
 
VMUG Japan book vsan 20250515 CPU/Memory vSAN
Kazuhiro Sota
 
2025_7_25_吉祥寺_設計ナイト_ADR運用におけるデータ利活用の考え方.pptx
ssuserfcafd1
 
20250726_Devinで変えるエンプラシステム開発の未来
Masaki Yamakawa
 
20250729_Devin-for-Enterprise
Masaki Yamakawa
 
baserCMS『カスタムコンテンツ』徹底活用術〜あなただけの管理画面を自由自在に〜
Ryuji Egashira
 
MahiroYoshida_セリフに着目したキャラクタロール推定に関する基礎検討_sigcc12th2025
Matsushita Laboratory
 

Effective Modern C++ 勉強会#3 Item16

  • 1. Effective Modern C++ 勉強会#3 Item 16 刈谷 満(@kariya_mitsuru) 2015/4/22
  • 2. Item 16: Make const member functions thread safe. const メンバ関数を スレッドセーフにしよう
  • 3. Item 16: Make const member functions thread safe. 例 多項式を表すクラス ⇒Polynomial その多項式の根を計算するメンバ関数 ⇒roots
  • 4. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const; … }; 多項式の根を計算するメンバ関数は、多項式自体を変更しない ↓ const メンバ関数 根を保持するデータ構造 (“using” については Item 9 を参照)
  • 5. Item 16: Make const member functions thread safe. 多項式の根の計算は高くつく。 ⇓ 必要なければ計算したくない。 でも 2 回以上は計算したくない。 ⇓ 最初に使われた時に計算しよう! 計算した根はキャッシュしよう!
  • 6. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: bool rootsAreValid{ false }; RootsType rootVals{}; };
  • 7. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: bool rootsAreValid{ false }; RootsType rootVals{}; }; キャッシュが有効じゃなかったら…
  • 8. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: bool rootsAreValid{ false }; RootsType rootVals{}; }; キャッシュが有効じゃなかったら… 根を計算してそれを rootVals に保存し…
  • 9. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: bool rootsAreValid{ false }; RootsType rootVals{}; }; キャッシュが有効じゃなかったら… 根を計算してそれを rootVals に保存し… キャッシュを有効にする!
  • 10. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: bool rootsAreValid{ false }; RootsType rootVals{}; }; キャッシュが有効じゃなかったら… 根を計算してそれを rootVals に保存し… キャッシュを有効にする! あれ? const メンバ関数じゃなかったっけ?
  • 11. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: mutable bool rootsAreValid{ false }; mutable RootsType rootVals{}; }; mutable! roots は概念的にはオブジェクトを変更しない(論理的 const 性) 詳しくはWebで!! Effective C++ 第3版、Item 3 参照
  • 12. Item 16: Make const member functions thread safe. ところで、この Polynomial クラスのオブジェクトを複数スレッドで同時に使うと… Polynomial p; … スレッド2 auto valsGivingZero = p.roots(); スレッド1 auto rootsOfP = p.roots(); const メンバ関数だから読むだけだし、 同期しなくても大丈夫っしょ!!
  • 13. Item 16: Make const member functions thread safe. ところで、この Polynomial クラスのオブジェクトを複数スレッドで同時に使うと… Polynomial p; … スレッド2 auto valsGivingZero = p.roots(); スレッド1 auto rootsOfP = p.roots(); const メンバ関数だから読むだけだし、 同期しなくても大丈夫っしょ!! 死
  • 14. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: mutable bool rootsAreValid{ false }; mutable RootsType rootVals{}; }; スレッド2スレッド1
  • 15. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: mutable bool rootsAreValid{ false }; mutable RootsType rootVals{}; }; スレッド2スレッド1 ①rootsAreValid を読む ⇓ まだ false
  • 16. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: mutable bool rootsAreValid{ false }; mutable RootsType rootVals{}; }; スレッド2スレッド1 ①rootsAreValid を読む ⇓ まだ false ②rootsAreValid を読む ⇓ まだ false!
  • 17. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: mutable bool rootsAreValid{ false }; mutable RootsType rootVals{}; }; スレッド2スレッド1 ①rootsAreValid を読む ⇓ まだ false ③計算しては書き込む! (まだ false) ②rootsAreValid を読む ⇓ まだ false!
  • 18. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: mutable bool rootsAreValid{ false }; mutable RootsType rootVals{}; }; スレッド2スレッド1 ①rootsAreValid を読む ⇓ まだ false ③計算しては書き込む! (まだ false) ②rootsAreValid を読む ⇓ まだ false! ④計算しては書き込む! (まだ false)
  • 19. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: mutable bool rootsAreValid{ false }; mutable RootsType rootVals{}; }; スレッド2スレッド1 ①rootsAreValid を読む ⇓ まだ false ③計算しては書き込む! (まだ false) ②rootsAreValid を読む ⇓ まだ false! ④計算しては書き込む! (まだ false) データレース!
  • 20. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { … rootsAreValid = true; } return rootVals; } private: mutable bool rootsAreValid{ false }; mutable RootsType rootVals{}; }; スレッド2スレッド1 ①rootsAreValid を読む ⇓ まだ false ③計算しては書き込む! (まだ false) ②rootsAreValid を読む ⇓ まだ false! ④計算しては書き込む! (まだ false) データレース!ようこそUBへ!
  • 21. Item 16: Make const member functions thread safe. ここでの問題は… roots は const メンバ関数なのに、スレッドセーフじゃない。 ⇓ const メンバ関数であることは C++11 でも C++98 でも正しい。 (rootsは多項式の値を変更しない) ⇓ 直すべきはスレッドセーフ性の欠如
  • 22. Item 16: Make const member functions thread safe. 解決策 mutex を使え
  • 23. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { std::lock_guard<std::mutex> g(m); ここは前と一緒… return rootVals; } private: mutable std::mutex m; ここも前と一緒… }; lock_guard を使って m をロック! ブロックの終わりで m をアンロック! ここの mutable にも注意! ロック、アンロックは const メンバ関数じゃない でも、m が変わっても多項式はやっぱり変わらない!
  • 24. Item 16: Make const member functions thread safe. Caution!(Errataあり) std::mutex はコピーもムーブもできない。 Polynomial に m を追加した結果、 コピーもムーブも出来なくなった。
  • 25. Item 16: Make const member functions thread safe. Caution!(Errataあり) std::mutex はコピーもムーブもできない。 Polynomial に m を追加した結果、 コピーもムーブも出来なくなった。 独自のコピー、ムーブコンストラ クタ、代入演算子を定義すれば いいと思うんだけど…
  • 26. Item 16: Make const member functions thread safe. コメントあり もし Polynomial が変更不可だったら、std::call_once と std::once_flag を使う方がいんじゃね? ⇓ ちょっと書いてみました
  • 27. Item 16: Make const member functions thread safe. class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { std::call_once(flag, [this]{ calc(); }); return rootVals; } void calc() const { … } private: mutable std::once_flag flag{}; mutable RootsType rootVals{}; }; call_once を使って calc を一回だけ呼ぶ (λ をつかってる理由は Item 34 参照) 実際に計算、格納するメンバ関数 もちろん const メンバ関数 もちろん once_flag も mutable!
  • 28. Item 16: Make const member functions thread safe. 場合によっては mutex は重過ぎ 例)メンバ関数が呼ばれた回数を数えたい ⇓ std::atomic で十分 (実際に std::atomic の方が mutex より軽いかどうかは、 動作するハードウェアと標準ライブラリの実装によるので注意!)
  • 29. Item 16: Make const member functions thread safe. class Point { public: … double distanceFromOrigin() const noexcept { ++callCount; return std::hypot(x, y); } private: mutable std::atomic<unsigned> callCount{ 0 }; double x, y; }; アトミック(不可分)なインクリメント (Item 40 参照)
  • 30. Item 16: Make const member functions thread safe. class Point { public: … double distanceFromOrigin() const noexcept { ++callCount; return std::hypot(x, y); } private: mutable std::atomic<unsigned> callCount{ 0 }; double x, y; }; 例によって、std::atomic<T> も コピーはおろかムーブも 出来ないので注意! (本書Errata)
  • 31. Item 16: Make const member functions thread safe. class Point { // 二次元の点 public: … double distanceFromOrigin() const noexcept // noexcept については { ++callCount; return std::hypot(x, y); } private: mutable std::atomic<unsigned> callCount{ 0 }; double x, y; }; あ、あと、std::hypotも わりとデキる奴なので 忘れないであげてください!(><) (本書Errata) hypotenuse squared
  • 32. Item 16: Make const member functions thread safe. でも… mutex より std::atomic の方が軽いからと言って、 使いすぎは禁物! 例)重い計算結果のキャッシュ(int)と キャッシュの有効フラグ(bool)
  • 33. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cachedValue = val1 + val2; cacheValid = true; return cachedValue; } } private: mutable std::atomic<bool> cacheValid{ false }; mutable std::atomic<int> cachedValue; }; キャッシュする値と その有効フラグを atomic にしました!
  • 34. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cachedValue = val1 + val2; cacheValid = true; return cachedValue; } } private: mutable std::atomic<bool> cacheValid{ false }; mutable std::atomic<int> cachedValue; }; キャッシュする値と その有効フラグを atomic にしました! ホントに 大丈夫…?
  • 35. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cachedValue = val1 + val2; cacheValid = true; return cachedValue; } } private: mutable std::atomic<bool> cacheValid{ false }; mutable std::atomic<int> cachedValue; }; スレッド2スレッド1
  • 36. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cachedValue = val1 + val2; cacheValid = true; return cachedValue; } } private: mutable std::atomic<bool> cacheValid{ false }; mutable std::atomic<int> cachedValue; }; スレッド2スレッド1 ①有効か確認する ⇓ まだ無効
  • 37. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cachedValue = val1 + val2; cacheValid = true; return cachedValue; } } private: mutable std::atomic<bool> cacheValid{ false }; mutable std::atomic<int> cachedValue; }; スレッド2スレッド1 ②計算して格納 (まだ無効) ①有効か確認する ⇓ まだ無効
  • 38. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cachedValue = val1 + val2; cacheValid = true; return cachedValue; } } private: mutable std::atomic<bool> cacheValid{ false }; mutable std::atomic<int> cachedValue; }; スレッド2スレッド1 ②計算して格納 (まだ無効) ③有効か確認する ⇓ まだ無効 ①有効か確認する ⇓ まだ無効
  • 39. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cachedValue = val1 + val2; cacheValid = true; return cachedValue; } } private: mutable std::atomic<bool> cacheValid{ false }; mutable std::atomic<int> cachedValue; }; スレッド2スレッド1 ②計算して格納 (まだ無効) ③有効か確認する ⇓ まだ無効 ①有効か確認する ⇓ まだ無効 ④計算して格納 (まだ無効)
  • 40. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cachedValue = val1 + val2; cacheValid = true; return cachedValue; } } private: mutable std::atomic<bool> cacheValid{ false }; mutable std::atomic<int> cachedValue; }; スレッド2スレッド1 ②計算して格納 (まだ無効) ③有効か確認する ⇓ まだ無効 ①有効か確認する ⇓ まだ無効 ④計算して格納 (まだ無効)残念!!!
  • 41. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; じゃあじゃあ、 キャッシュの有効化と 値の代入を逆にしよう!
  • 42. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; じゃあじゃあ、 キャッシュの有効化と 値の代入を逆にしよう! ホントにホントに 大丈夫…?
  • 43. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; スレッド2スレッド1
  • 44. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; スレッド2スレッド1 ①有効か確認する ⇓ まだ無効
  • 45. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; スレッド2スレッド1 ②2つとも計算する (まだ無効) ①有効か確認する ⇓ まだ無効
  • 46. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; スレッド2スレッド1 ②2つとも計算する (まだ無効) ③有効か確認する ⇓ まだ無効 ①有効か確認する ⇓ まだ無効
  • 47. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; スレッド2スレッド1 ②2つとも計算する (まだ無効) ③有効か確認する ⇓ まだ無効 ①有効か確認する ⇓ まだ無効 ④2つとも計算する (まだ無効)
  • 48. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; スレッド2スレッド1 ②2つとも計算する (まだ無効) ③有効か確認する ⇓ まだ無効 ①有効か確認する ⇓ まだ無効 ④2つとも計算する (まだ無効) 残念!!! (本書Errata)
  • 49. Item 16: Make const member functions thread safe. でも… 実はもっとヤバい…
  • 50. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; スレッド2スレッド1
  • 51. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; スレッド2スレッド1 ①有効か確認する ⇓ まだ無効
  • 52. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; スレッド2スレッド1 ②2つとも計算する (まだ無効) ①有効か確認する ⇓ まだ無効
  • 53. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; スレッド2スレッド1 ②2つとも計算する (まだ無効) ③有効にする! (まだ格納前!) ①有効か確認する ⇓ まだ無効
  • 54. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; スレッド2スレッド1 ②2つとも計算する (まだ無効) ③有効にする! (まだ格納前!) ④有効か確認する ⇓ 有効!? 値を返す!!! ①有効か確認する ⇓ まだ無効
  • 55. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cacheValid = true; return cachedValue = val1 + val2; } } … }; スレッド2スレッド1 ②2つとも計算する (まだ無効) ③有効にする! (まだ格納前!) ④有効か確認する ⇓ 有効!? 値を返す!!! ①有効か確認する ⇓ まだ無効 死
  • 56. Item 16: Make const member functions thread safe. 教訓 単一の変数やメモリ位置の同期が必要 ⇒ std::atomic 2 つ以上の変数やメモリ位置に対して不可分操作が必要 ⇒ mutex(std::atomic じゃNG!)
  • 57. Item 16: Make const member functions thread safe. class Widget { public: … int magicValue() const { std::lock_guard<std::mutex> guard(m); if (cacheValid) return cachedValue; else { auto val1 = expensiveComputation1(); auto val2 = expensiveComputation2(); cachedValue = val1 + val2; cacheValid = true; return cachedValue; } } … private: mutable std::mutex m; mutable int cachedValue; mutable bool cacheValid{ false }; }; lock_guard を使って m をロック! ブロックの終わりで m をアンロック! 例によって mutable! もはや atomic の必要なし!
  • 58. Item 16: Make const member functions thread safe. ところで… 散々マルチスレッドの話をしてきたけど、 シングルスレッド前提の場合は? ⇓ これらの対応は不要 (その場合、mutex や std::atomic に 関するコストを避けられる)
  • 59. Item 16: Make const member functions thread safe. ところで… 散々マルチスレッドの話をしてきたけど、 シングルスレッド前提の場合は? ⇓ これらの対応は不要 (その場合、mutex や std::atomic に 関するコストを避けられる)
  • 60. Item 16: Make const member functions thread safe. え ー マ ジ マ ル チ ス レ ッ ド 未 対 応 ⁉ マ ル チ ス レ ッ ド 未 対 応 が 許 さ れ る の は 小 学 生 ま で だ よ ね ー ©キモイガールズ
  • 61. Item 16: Make const member functions thread safe. 覚えておくこと • 並行コンテキストで決して使用しないと確信できない限り、 const メンバ関数をスレッドセーフにしておくこと。 • std::atomic の使用は mutex よりも良いパフォーマンスを示す かもしれないが、それは単一の変数やメモリ位置への操作のみに適 している。