C++ 原子操作 std::atomic<T>

本文通过实例详细介绍了C++中std::atomic&lt;T&gt;模板类的使用,展示了如何利用原子操作解决多线程环境下的竞态条件,避免数据不一致问题。通过对比普通变量与原子变量在多线程环境下的行为,强调了原子操作的重要性。

C++ 原子操作 std::atomic<T>

std::atomic<T>模板类可以使对象操作为原子操作,避免多线程竞争问题;请看如下代码,一目了然:

复制代码

class Test
{
public:    
    Test() = default;

    void CThreadFunc()
    {
        for (int i = 0; i < 10000; ++i)
        {
            //std::lock_guard<std::mutex> lck(Test::m_s_ivalue_mutex); //m_iValue需要加锁才可正常工作
            m_iValue++;

            m_atomic_value++;//不加锁,也可正常工作
        }
    }

    void Start()
    {
        std::vector<std::thread> threads;
        for (int i = 0; i < 10; ++i)
        {
            threads.push_back(std::thread(&Test::CThreadFunc, this));
        }

        for (auto& th : threads)
        {
            if (th.joinable())
            {
                th.join();
            }
        }
     std::cout << "m_iValue:" << m_iValue << ", m_atomic_value:" << m_atomic_value << std::endl;
    }

private:
    int m_iValue = 0;
    std::atomic<int> m_atomic_value = 0;//sta::atomic<T> 原子操作
    static std::mutex m_s_ivalue_mutex;
};

复制代码

 

 

 

 

 

 

 

执行:

Test test;
test.Start();

C++ 中,`std::vector<std::atomic<bool>>` 的使用涉及线程安全性和性能问题。`std::atomic<bool>` 提供了原子操作,确保对布尔值的读写在多线程环境下是安全的,而 `std::vector` 作为动态数组,允许在运行时调整大小。然而,将 `std::atomic<bool>` 作为 `std::vector` 的元素类型时,需要注意一些关键问题。 ### 线程安全性 `std::atomic<bool>` 保证了单个变量的原子性,即对单个 `std::atomic<bool>` 的操作(如加载和存储)是线程安全的。这意味着多个线程可以同时读写不同的 `std::atomic<bool>` 元素而不会导致数据竞争。然而,如果多个线程同时修改同一个 `std::atomic<bool>` 元素,则仍然需要确保这些操作的顺序性,例如通过使用内存顺序(`memory_order`)参数来控制操作的可见性[^3]。 另一方面,`std::vector` 本身并不是线程安全的。如果多个线程同时对 `std::vector` 进行修改(例如通过 `push_back` 或 `resize`),则必须使用互斥锁(`std::mutex`)或其他同步机制来保护这些操作。这是因为 `std::vector` 在调整大小时可能会重新分配内存,导致所有元素的地址发生变化,从而引发未定义行为[^1]。 ### 性能影响 使用 `std::vector<std::atomic<bool>>` 可能会对性能产生一定影响。首先,`std::atomic<bool>` 的操作通常比普通布尔变量更昂贵,因为它们需要确保内存顺序的一致性。其次,`std::vector` 的动态内存管理可能导致额外的开销,尤其是在频繁调整大小的情况下。为了减少这种开销,可以在初始化时预留足够的容量[^5]。 此外,`std::vector<std::atomic<bool>>` 的布局可能会导致缓存行伪共享(false sharing)问题。如果多个 `std::atomic<bool>` 元素位于同一个缓存行中,并且被不同的线程频繁修改,则可能导致性能下降。为了避免这种情况,可以通过使用 `std::array<std::atomic<bool>, N>` 或者手动对齐元素来确保每个 `std::atomic<bool>` 占据独立的缓存行[^2]。 ### 正确用法 为了正确使用 `std::vector<std::atomic<bool>>`,建议遵循以下最佳实践: - **避免在调整大小时并发访问**:如果多个线程需要访问 `std::vector<std::atomic<bool>>`,应在初始化时确定其大小,并避免在运行时动态调整大小。如果必须调整大小,则应在修改期间使用互斥锁进行同步。 - **使用适当的内存顺序**:在对 `std::atomic<bool>` 进行操作时,应根据需求选择合适的内存顺序(如 `std::memory_order_relaxed`、`std::memory_order_acquire`、`std::memory_order_release` 等),以平衡线程安全性和性能[^4]。 - **考虑替代方案**:如果 `std::vector<std::atomic<bool>>` 的性能无法满足需求,可以考虑使用其他数据结构,如 `std::array<std::atomic<bool>, N>` 或自定义的位字段结构,以减少内存开销并提高缓存效率[^2]。 ### 示例代码 以下是一个简单的示例,展示了如何在多线程环境中使用 `std::vector<std::atomic<bool>>`: ```cpp #include <iostream> #include <vector> #include <atomic> #include <thread> void set_flag(std::vector<std::atomic<bool>>& flags, size_t index) { flags[index].store(true, std::memory_order_release); } void check_flags(const std::vector<std::atomic<bool>>& flags) { for (size_t i = 0; i < flags.size(); ++i) { if (flags[i].load(std::memory_order_acquire)) { std::cout << "Flag at index " << i << " is set." << std::endl; } } } int main() { const size_t num_flags = 5; std::vector<std::atomic<bool>> flags(num_flags); std::thread t1(set_flag, std::ref(flags), 0); std::thread t2(set_flag, std::ref(flags), 2); std::thread t3(set_flag, std::ref(flags), 4); t1.join(); t2.join(); t3.join(); check_flags(flags); return 0; } ``` 在这个示例中,多个线程分别设置 `std::vector<std::atomic<bool>>` 中的不同元素,主线程随后检查这些元素的状态。通过使用 `std::memory_order_release` 和 `std::memory_order_acquire`,确保了线程间的数据可见性。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值