std::atomic: ロックフリー同期

mutex より軽量。カウンタやフラグに最適。

std::atomic
ロックフリーのスレッドセーフ型。
CAS
Compare-And-Swap。条件付き交換。
ロックフリー
mutex なしで同期。

atomic 基礎

電灯のスイッチ (Light Switch)

atomic変数は「電灯のスイッチ」です。ONかOFFか、どちらかしかありません。「半分だけON」という中間状態は存在しません。鍵(mutex)を使わずに、誰かがスイッチを押せばチャタリングなしで瞬時に切り替わります(不可分操作)。

通常の `count++` は「読み込み→加算→書き込み」の3ステップで行われるため、途中で他スレッドが割り込むとデータが壊れます。`std::atomic` はこれを CPU の命令レベルで1つの操作として行うため、ロックなし(Lock-Free)で安全に共有できます。

Atomic Operations
#include <atomic>
std::atomic<int> counter{0};
// スレッドセーフなインクリメント
void increment() {
counter++; // アトミック操作
counter.fetch_add(1); // 明示的に
}
// 読み書き
int val = counter.load();
counter.store(100);
// 比較交換(CAS)
int expected = 0;
bool success = counter.compare_exchange_strong(expected, 1);
実行結果
counter++ → アトミックに+1\nload() / store() で安全に読み書き
Bad
// ❌ Bad: mutex でカウンタ保護
std::mutex m;
int counter = 0;
{
std::lock_guard lock(m);
counter++; // 重い
}
Good
// ✅ Good: atomic
std::atomic<int> counter{0};
counter++; // ロックフリー

パターン

Flags, CAS, wait/notify
// フラグ
std::atomic<bool> done{false};
// スレッド1
done.store(true);
// スレッド2
while (!done.load()) { }
// メモリオーダー(上級)
counter.store(42, std::memory_order_release);
int val = counter.load(std::memory_order_acquire);
// atomic_flag(最も軽量)
std::atomic_flag flag = ATOMIC_FLAG_INIT;
while (flag.test_and_set()) { } // スピンロック
// クリティカルセクション
flag.clear();
// C++20: wait/notify
std::atomic<int> signal{0};
signal.wait(0); // 0以外になるまで待機
signal.store(1);
signal.notify_one(); // 待機を解除
Tip: シンプルなカウンタやフラグには atomic。複雑な操作には mutex。

合格ライン

load/store を使える
atomic と mutex を使い分けられる

参考リンク

演習課題

課題1: atomic カウンタ
std::atomic<int> で複数スレッドから安全にカウンタをインクリメントしてください。
課題2: CAS
compare_exchange_weak を使って条件付き更新を実装してください。

次のステップ