什麼是原子操作?
在多執行緒程式中,執行緒之間可能同時訪問和修改共享資料。
如果沒有適當的同步機制,這可能導致資料競爭和未定義行為。
我們在過去最常使用鎖(如 std::mutex)來保護共享資料,
但鎖可能帶來性能開銷和死鎖風險。
原子操作提供了一種輕量級的同步方式,
允許我們在不使用鎖的情況下進行安全的共享資料訪問。
一段使用 std::atomic 的範例:
Code:
#include <atomic>
#include <thread>
#include <vector>
#include <iostream>
std::atomic<int> counter(0);
void increment(int numIncrements) {
for (int i = 0; i < numIncrements; ++i) {
counter.fetch_add(1, std::memory_order_relaxed);
}
}
int main() {
const int numThreads = 4;
const int numIncrements = 100000;
std::vector<std::thread> threads;
for (int i = 0; i < numThreads; ++i) {
threads.emplace_back(increment, numIncrements);
}
for (auto& t : threads) {
t.join();
}
std::cout << "Final counter value: " << counter.load() << std::endl;
// 最終 counter 的值應該是 numThreads * numIncrements
return 0;
}
C++中的 std::atomic
C++11 引入了標準庫 <atomic>,提供了一組模板類別和函數,用於實現原子操作。
std::atomic 是一個模板類別,可以包裝基本類別或自定義類別,使其操作具有原子性。
原子操作的原理就是確保不會同時有第二個執行緒存取資源,而之所以被稱為原子操作,
也是因為原子作為最小不可分割的個體(一般常識上,請忽略夸克等更小的基本粒子),
只要是std::atomic支援的型別與原子性的操作(如store、load、exchange或是運算符號
++,–,+=,-=,&=,|=,^= 等等)就可以進行無鎖操作,
但不包含這種單一操作多次使用:
Code:
if (atomicVar.load() == expectedValue) {
atomicVar.store(newValue);
}
因為在 load 與 store 中間可能會有其他原子操作出現。
另外要注意的是記憶體順序(memory_order),
因為原子操作可能為了更高效能使用較脆弱的記憶體順序(如 memory_order_relaxed)