博客 / 詳情

返回

每日一個C++知識點|原子操作

上一篇文章講了C++多線程的基礎知識, 今天我們來講講原子操作, 原子操作也是C++多線程的主要內容

什麼是原子操作

什麼是原子操作呢? 就是一個操作執行像原子一樣不可再分割, 在多線程環境中就不會被其他線程打斷, 因而就會保證某個操作執行的連續性和完整性

也就是説,一個操作要麼全部執行完畢,要麼完全不執行

如果沒有原子操作

如果沒有原子操作的話, 在多線程環境中會導致數據讀寫混亂, 下面讓我們用代碼舉個經典的反例看看

#include <iostream>
#include <thread>
#include <vector>

int count = 0; // 普通整型變量

// 線程執行的函數:count自增1000次
void increment() {
    for (int i = 0; i < 1000; ++i) {
        count++; // 看似簡單的自增,實則暗藏危機
    }
}

int main() {
    std::vector<std::thread> threads;

    // 創建10個線程
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(increment);
    }

    // 等待所有線程執行完畢
    for (auto& t : threads) {
        t.join();
    }

    std::cout << "最終count值:" << count << std::endl; // 結果遠小於10000

    return 0;
}

上述代碼的基本內容是讓10個線程各自給count加1000次,但實際運行成果總小於1000

是什麼原因呢?
因為count++這個操作,在CPU層面被拆成三步:

  1. 從內存中讀取count的值存到寄存器
  2. 寄存器中的值+1
  3. 把新值寫回內存

因為這三步不是"不可分割的",所以容易造成讀寫混亂,線程A剛讀完count,還沒有在寄存器+1,線程B就插進來讀了同一個值,然後各自+1寫回,相當於白加了一次,導致數據被覆蓋, 這就顯得原子操作是多麼重要了, 下面讓我們來看看原子操作是怎麼使用的~

原子操作的使用

核心工具

std::atomic:它是 C++11 開始提供的原子類型模板,不需要手動加鎖,也可以實現線程安全

常用方法

以int類型變量count為例

  1. 讀值count.load()
  2. 寫值count.store(10)
  3. 自增count.fetch_add(1)
  4. 交換count.exchange(20)

下面讓我們用原子操作來修復上述的例子

#include <iostream>
#include <thread>
#include <vector>
#include <atomic> // 包含原子操作的頭文件

std::atomic<int> count = 0; // 原子整型變量

// 線程執行的函數:count自增1000次
void increment() {
    for (int i = 0; i < 1000; ++i) {
        // 方式1:使用fetch_add原子自增
        count.fetch_add(1);
        // 方式2:直接用count++(重載後的原子操作,效果一樣)
        // count++;
    }
}

int main() {
    std::vector<std::thread> threads;

    // 創建10個線程
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(increment);
    }

    // 等待所有線程執行完畢
    for (auto& t : threads) {
        t.join();
    }

    // 原子變量可以直接輸出,底層會調用load()
    std::cout << "最終count值:" << count << std::endl; // 結果一定是10000

    return 0;
}

運行結果如下:

這次運行後,count的值10000,使用了原子操作後不會被其他線程打斷

這就是原子操作在多線程場景中的使用意義

總結

  1. 原子操作的核心是不可分割,能避免多線程下的數椐競爭
  2. 原子操作通過std::atomic實現

那麼我們在上一篇多線程基礎的文章中講到了互斥鎖,也可以解決數據競態條件的問題,那麼原子操作和互斥鎖又有什麼區別和聯繫呢?他們誰的性能更強呢?如果點贊量超過1,我下期將會重點講講原子操作和互斥鎖之間的關係~

希望這篇文章能幫你搞懂 C++ 原子操作!如果覺得有用,別忘了點贊喲

或者關注本人,我將會持續分享高質量的內容~

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.