C++11——原子變量


轉載來自:https://subingwen.cn/cpp/atomic/#2-2-%E5%8E%9F%E5%AD%90%E5%8F%98%E9%87%8F%E7%89%88%E6%9C%AC

C++11 提供了一個原子類型 std::atomic<T>,通過這個原子類型管理的內部變量就可以稱之為原子變量,我們可以給原子類型指定任意的類型作為模板參數,因此原子變量也可以是任意的類型。

C++11 內置了整形的原子變量,這樣就可以更方便的使用原子變量了。在多線程操作中,使用原子變量之后就不需要再使用互斥量來保護該變量了,用起來更簡潔。因為對原子變量進行的操作只能是一個原子操作(atomic operation),原子操作指的是不會被線程調度機制打斷的操作,這種操作一旦開始,就一直運行到結束,中間不會有任何的上下文切換多線程同時訪問共享資源造成數據混亂的原因就是因為 CPU 的上下文切換導致的,使用原子變量解決了這個問題,因此互斥鎖的使用也就不再需要了。

1. atomic 類成員
類定義

// 定義於頭文件 <atomic>
template< class T >
struct atomic;

通過定義可得知:在使用這個模板類的時候,一定要指定模板類型。

構造函數

//
atomic() noexcept = default;
//
constexpr atomic( T desired ) noexcept;
//
atomic( const atomic& ) = delete;
構造函數①:默認無參構造函數。
構造函數②:使用 desired 初始化原子變量的值。
構造函數③:使用 =delete 顯示刪除拷貝構造函數,不允許進行對象之間的拷貝

公共成員函數

原子類型在類內部重載了 = 操作符,並且不允許在類的外部使用 = 進行對象的拷貝

T operator=( T desired ) noexcept;
T operator=( T desired ) volatile noexcept;

atomic& operator=( const atomic& ) = delete;
atomic& operator=( const atomic& ) volatile = delete;

原子地以 desired 替換當前值。按照 order 的值影響內存。

void store( T desired, std::memory_order order = std::memory_order_seq_cst ) noexcept;
void store( T desired, std::memory_order order = std::memory_order_seq_cst ) volatile noexcept;

desired:存儲到原子變量中的值
order:強制的內存順序
原子地加載並返回原子變量的當前值。按照 order 的值影響內存。直接訪問原子對象也可以得到原子變量的當前值。

T load( std::memory_order order = std::memory_order_seq_cst ) const noexcept;
T load( std::memory_order order = std::memory_order_seq_cst ) const volatile noexcept;

 

C++20 新增成員

在 C++20 版本中添加了新的功能函數,可以通過原子類型來阻塞線程,和條件變量中的等待 / 通知函數是一樣的。

公共成員函數 說明
wait(C++20) 阻塞線程直至被提醒且原子值更改
notify_one(C++20) 通知(喚醒)至少一個在原子對象上阻塞的線程
notify_all(C++20) 通知(喚醒)所有在原子對象上阻塞的線程
類型別名

別名 原始類型定義
atomic_bool(C++11) std::atomic<bool>
atomic_char(C++11) std::atomic<char>
atomic_schar(C++11) std::atomic<signed char>
atomic_uchar(C++11) std::atomic<unsigned char>
atomic_short(C++11) std::atomic<short>
atomic_ushort(C++11) std::atomic<unsigned short>
atomic_int(C++11) std::atomic<int>
atomic_uint(C++11) std::atomic<unsigned int>
atomic_long(C++11) std::atomic<long>
atomic_ulong(C++11) std::atomic<unsigned long>
atomic_llong(C++11) std::atomic<long long>
atomic_ullong(C++11) std::atomic<unsigned long long>
atomic_char8_t(C++20) std::atomic<char8_t>
atomic_char16_t(C++11) std::atomic<char16_t>
atomic_char32_t(C++11) std::atomic<char32_t>
atomic_wchar_t(C++11) std::atomic<wchar_t>
atomic_int8_t(C++11)(可選) std::atomic<std::int8_t>
atomic_uint8_t(C++11)(可選) std::atomic<std::uint8_t>
atomic_int16_t(C++11)(可選) std::atomic<std::int16_t>
atomic_uint16_t(C++11)(可選) std::atomic<std::uint16_t>
atomic_int32_t(C++11)(可選) std::atomic<std::int32_t>
atomic_uint32_t(C++11)(可選) std::atomic<std::uint32_t>
atomic_int64_t(C++11)(可選) std::atomic<std::int64_t>
atomic_uint64_t(C++11)(可選) std::atomic<std::uint64_t>
atomic_int_least8_t(C++11) std::atomic<std::int_least8_t>
atomic_uint_least8_t(C++11) std::atomic<std::uint_least8_t>
atomic_int_least16_t(C++11) std::atomic<std::int_least16_t>
atomic_uint_least16_t(C++11) std::atomic<std::uint_least16_t>
atomic_int_least32_t(C++11) std::atomic<std::int_least32_t>
atomic_uint_least32_t(C++11) std::atomic<std::uint_least32_t>
atomic_int_least64_t(C++11) std::atomic<std::int_least64_t>
atomic_uint_least64_t(C++11) std::atomic<std::uint_least64_t>
atomic_int_fast8_t(C++11) std::atomic<std::int_fast8_t>
atomic_uint_fast8_t(C++11) std::atomic<std::uint_fast8_t>
atomic_int_fast16_t(C++11) std::atomic<std::int_fast16_t>
atomic_uint_fast16_t(C++11) std::atomic<std::uint_fast16_t>
atomic_int_fast32_t(C++11) std::atomic<std::int_fast32_t>
atomic_uint_fast32_t(C++11) std::atomic<std::uint_fast32_t>
atomic_int_fast64_t(C++11) std::atomic<std::int_fast64_t>
atomic_uint_fast64_t(C++11) std::atomic<std::uint_fast64_t>
atomic_intptr_t(C++11)(可選) std::atomic<std::intptr_t>
atomic_uintptr_t(C++11)(可選) std::atomic<std::uintptr_t>
atomic_size_t(C++11) std::atomic<std::size_t>
atomic_ptrdiff_t(C++11) std::atomic<std::ptrdiff_t>
atomic_intmax_t(C++11) std::atomic<std::intmax_t>
atomic_uintmax_t(C++11) std::atomic<std::uintmax_t>
2. 原子變量的使用
假設我們要制作一個多線程交替數數的計數器,我們使用互斥鎖和原子變量的方式分別進行實現,對比一下二者的差異:

2.1 互斥鎖版本

#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
#include <functional>
using namespace std;

struct Counter
{
    void increment()
    {
        for (int i = 0; i < 10; ++i)
        {
            lock_guard<mutex> locker(m_mutex);
            m_value++;
            cout << "increment number: " << m_value
                << ", theadID: " << this_thread::get_id() << endl;
            this_thread::sleep_for(chrono::milliseconds(100));
        }
    }

    void decrement()
    {
        for (int i = 0; i < 10; ++i)
        {
            lock_guard<mutex> locker(m_mutex);
            m_value--;
            cout << "decrement number: " << m_value
                << ", theadID: " << this_thread::get_id() << endl;
            this_thread::sleep_for(chrono::milliseconds(100));
        }
    }

    int m_value = 0;
    mutex m_mutex;
};

int main()
{
    Counter c;
    auto increment = bind(&Counter::increment, &c);
    auto decrement = bind(&Counter::decrement, &c);
    thread t1(increment);
    thread t2(decrement);

    t1.join();
    t2.join();

    return 0;
}

 

2.2 原子變量版本

#include <iostream>
#include <thread>
#include <atomic>
#include <functional>
using namespace std;

struct Counter
{
    void increment()
    {
        for (int i = 0; i < 10; ++i)
        {
            m_value++;
            cout << "increment number: " << m_value
                << ", theadID: " << this_thread::get_id() << endl;
            this_thread::sleep_for(chrono::milliseconds(500));
        }
    }

    void decrement()
    {
        for (int i = 0; i < 10; ++i)
        {
            m_value--;
            cout << "decrement number: " << m_value
                << ", theadID: " << this_thread::get_id() << endl;
            this_thread::sleep_for(chrono::milliseconds(500));
        }
    }
    // atomic<int> == atomic_int
    atoimc_int m_value = 0;
};

int main()
{
    Counter c;
    auto increment = bind(&Counter::increment, &c);
    auto decrement = bind(&Counter::decrement, &c);
    thread t1(increment);
    thread t2(decrement);

    t1.join();
    t2.join();

    return 0;
}

 

通過代碼的對比可以看出,使用了原子變量之后,就不需要再定義互斥量了,在使用上更加簡便,並且這兩種方式都能保證在多線程操作過程中數據的正確性,不會出現數據的混亂。

原子類型 atomic<T> 可以封裝原始數據最終得到一個原子變量對象,操作原子對象能夠得到和操作原始數據一樣的效果,當然也可以通過 store() 和 load() 來讀寫原子對象內部的原始數據。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM