int
やbool
のような読み込みが原子的だと思われる型について、変更がある場所を全て排他制御されている場合でも、atomic
を使う必要はあるのでしょうか?
サンプルとして、下記のコードを見てください。
C++
1#include <atomic> 2#include <chrono> 3#include <iostream> 4#include <thread> 5 6int main() 7{ 8 std::atomic_int x(0); 9 int y(0); 10 std::mutex mw; 11 std::mutex mr; 12 std::thread thws[1000]; 13 std::thread thrs[1000]; 14 for (int i = 0; i < 1000; ++i) { 15 thws[i] = std::thread([&]() { 16 std::this_thread::sleep_for(std::chrono::seconds(1)); 17 std::lock_guard<std::mutex> lock(mw); 18 x++; 19 y++; 20 }); 21 thrs[i] = std::thread([&]() { 22 std::this_thread::sleep_for(std::chrono::seconds(1)); 23 int x_t = x; // ① 安全 24 int y_t = y; // ② 危険??? 25 std::lock_guard<std::mutex> lock(mr); 26 std::cout << x_t << "," << y_t << std::endl; 27 }); 28 } 29 for (int i = 0; i < 1000; ++i) { 30 thws[i].join(); 31 thrs[i].join(); 32 } 33 std::cout << x << "," << y << std::endl; 34 return 0; 35}
x
は原子的int
ですがy
は普通のint
です。しかし、インクリメントの処理について、mutexによりどちらも排他制御されているため、期待通りに最終的に1000になります(もし、mutexがなければ、y
は1000で無いときがあります)。
では、読み込み側はどうでしょうか?①は安全でしょう。x++
の最中にx
が読み込まれて壊れてしまうことはありません。では②はどうなのでしょうか?y++
の最中にy
が読み込まれた場合、おかしな値になることはあるのでしょうか?また、後置インクリメント以外、例えば、前置インクリメントや代入、+=
などの場合は、うまくいかないとかあるのでしょうか?そして、それらのことはint
以外の、bool
やlong long
等の他の整数型についても言えることなのでしょうか?(とりあえず、上のコードを手元で試す限り、y
がおかしな値を返すことはないようです。)
もし、問題が無ければ、atomicを外して僅かでも処理を速くしたいと考えています(排他制御自体は別の理由で外せないことが前提です)。変更が全て排他制御されていれば、atomicは外しても大丈夫なのかどうかを教えてください。
回答5件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/11/10 21:15
2016/11/11 02:45 編集