首页 > 编程语言 >C++之原子操作:实现高效、安全的多线程编程

C++之原子操作:实现高效、安全的多线程编程

时间:2023-07-08 11:25:08浏览次数:40  
标签:std include int 编程 C++ 原子 atomic 操作 多线程

背景

在多线程编程中,线程间的同步和数据竞争问题是无法避免的。传统的同步方法,如互斥锁(mutex)和条件变量(condition variable),可能导致性能下降和死锁等问题。C++11引入了原子操作,提供了一种更高效、安全的多线程编程方式。本文将介绍C++中的原子操作概念、使用方法及示例。

C++中的原子操作

C++11引入了头文件,提供了一系列原子类型和操作。原子类型包括std::atomic_flag、std::atomic和特化的原子整数类型(如std::atomic_int、std::atomic_long等)。原子操作包括原子读写、原子算术运算、原子位操作等。

原子类型

(1)std::atomic_flag

std::atomic_flag是最简单的原子类型,只有两种状态:设置(set)和清除(clear)。std::atomic_flag不可拷贝和赋值,且必须使用ATOMIC_FLAG_INIT宏初始化。

#include <atomic>

std::atomic_flag flag = ATOMIC_FLAG_INIT;

(2)std::atomic

std::atomic是通用的原子类型,可以用于任何可拷贝的类型T。std::atomic的初始化可以使用默认构造函数、拷贝构造函数或者赋值操作。

#include <atomic>

std::atomic<int> atomic_int(0); // 初始化为0

3)特化的原子整数类型

特化的原子整数类型是针对整数类型的原子类型,如std::atomic_int、std::atomic_long等。它们的使用方法与std::atomic相同。

#include <atomic>

std::atomic_int atomic_int(0); // 初始化为0

原子操作

(1)原子读写 原子读写操作包括load、store和exchange。load用于读取原子变量的值,store用于设置原子变量的值,exchange用于交换原子变量的值。

#include <atomic>
#include <iostream>

int main() {
    std::atomic<int> atomic_int(0);

    int value = atomic_int.load(); // 读取原子变量的值
    std::cout << "value: " << value << std::endl;

    atomic_int.store(10); // 设置原子变量的值
    std::cout << "value: " << atomic_int.load() << std::endl;

    int old_value = atomic_int.exchange(20); // 交换原子变量的值
    std::cout << "old_value: " << old_value << ", new_value: " << atomic_int.load() << std::endl;

    return 0;
}

(2)原子算术运算

原子算术运算包括fetch_add、fetch_sub、fetch_and、fetch_or和fetch_xor等。这些操作可以实现原子加、原子减、原子与、原子或和原子异或等功能。

#include <atomic>
#include <iostream>

int main() {
    std::atomic<int> atomic_int(0);

    int old_value = atomic_int.fetch_add(10); // 原子加操作 
    std::cout << "old_value: " << old_value << ", new_value: " << atomic_int.load() << std::endl;
    old_value = atomic_int.fetch_sub(5); // 原子减操作
std::cout << "old_value: " << old_value << ", new_value: " << atomic_int.load() << std::endl;

old_value = atomic_int.fetch_and(0b1100); // 原子与操作
std::cout << "old_value: " << old_value << ", new_value: " << atomic_int.load() << std::endl;

old_value = atomic_int.fetch_or(0b1010); // 原子或操作
std::cout << "old_value: " << old_value << ", new_value: " << atomic_int.load() << std::endl;

old_value = atomic_int.fetch_xor(0b1111); // 原子异或操作
std::cout << "old_value: " << old_value << ", new_value: " << atomic_int.load() << std::endl;

return 0;
}

(3)原子比较和交换

原子比较和交换操作(compare-and-swap,CAS)是一种常用的同步原语,用于实现无锁数据结构。std::atomic<T>提供了compare_exchange_weakcompare_exchange_strong两个CAS操作。

#include <atomic>
#include <iostream>

int main() {
    std::atomic<int> atomic_int(0);

    int expected = 0;
    bool success = atomic_int.compare_exchange_weak(expected, 10); // CAS操作
    std::cout << "success: " << success << ", expected: " << expected << ", value: " << atomic_int.load() << std::endl;

    expected = 0;
    success = atomic_int.compare_exchange_strong(expected, 20); // CAS操作
    std::cout << "success: " << success << ", expected: " << expected << ", value: " << atomic_int.load() << std::endl;

    return 0;
}

原子操作示例

下面是一个使用原子操作实现的多线程累加器示例。在这个示例中,我们创建了两个线程,分别对原子整数变量atomic_int进行累加操作。

#include <atomic>
#include <iostream>
#include <thread>

std::atomic<int> atomic_int(0);

void add(int n) {
    for (int i = 0; i < n; ++i) {
        atomic_int.fetch_add(1);
    }
}

int main() {
    std::thread t1(add, 100000);
    std::thread t2(add, 100000);

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

    std::cout << "atomic_int: " << atomic_int.load() << std::endl;

    return 0;
}

背景

通过本文的介绍,我们了解了C++中原子操作的概念、使用方法及示例。原子操作为多线程编程提供了一种高效、安全的方式,避免了传统同步方法的性能和死锁问题。在实际编程中,我们可以根据需求选择合适的原子类型和操作,实现无锁数据结构和并发算法。

标签:std,include,int,编程,C++,原子,atomic,操作,多线程
From: https://www.cnblogs.com/blizzard8204/p/17536926.html

相关文章

  • C++之深入探讨同步操作与强制次序
    背景在C++多线程编程中,线程间的同步与顺序执行是至关重要的。同步操作可以确保线程间的数据一致性,避免数据竞争和死锁问题。强制次序则可以确保线程间的操作按照预期顺序执行。本文将详细介绍C++多线程编程中的同步关系、先行关系、原子操作的内存顺序、释放序列和同步关系、栅栏......
  • C++之内存模型
    背景C++内存模型是C++程序中内存管理和数据存储的基础。了解C++内存模型的概念和运作机制对于编写高效、安全的C++代码至关重要。本文将详细介绍C++内存模型的基本概念、内存分配策略以及与其相关的代码示例。C++内存模型的基本概念C++内存模型主要包括以下几个部分:静态存储......
  • C++之锁
    背景在C++多线程编程中,锁是一种常用的同步原语,用于保护共享数据的访问。C++标准库提供了多种锁类型,适用于不同的使用场景。在这篇博客中,我们将介绍C++中的各种锁类型,比较它们的特点,并探讨不同锁在实际应用中的使用场景。std::mutexstd::mutex是C++标准库中最基本的互斥锁类型,它......
  • C++之future
    背景在C++多线程编程中,同步线程间的操作和结果通常是一个关键问题。C++11引入了std::future这一同步原语,用于表示异步操作的结果。本文将介绍C++中std::future的使用方法、优势以及与其他同步方法的对比。使用std::futurestd::future表示一个异步操作的结果,可以用于获取操作的......
  • C++之条件竞争
    背景在多线程编程中,线程间共享数据是一种常见的情况。然而,如果不加以处理,共享数据可能导致一些问题,如条件竞争。在这篇博客中,我们将介绍C++线程共享数据的问题,包括条件竞争的概念以及防止恶性条件竞争的方法。什么是条件竞争?条件竞争(RaceCondition)是指多个线程在访问和操作共......
  • C++之共享数据
    背景在C++多线程编程中,线程间共享数据是一种常见的情况。然而,如果不加以处理,共享数据可能导致一些问题,如条件竞争。本文将介绍C++中多线程共享数据的方式,包括各种方式的使用场景和比较。使用互斥锁(Mutex)互斥锁(Mutex)是一种同步原语,用于保护共享数据的访问。当一个线程访问共享数......
  • C++之死锁
    背景在多线程编程中,死锁是一个常见的问题,它会导致程序陷入无法继续执行的状态。在这篇博客中,我们将介绍C++中死锁的概念、产生原因以及解决办法。什么是死锁?死锁是指多个线程在等待对方释放资源,导致彼此都无法继续执行的情况。死锁通常发生在多个线程同时锁定多个互斥锁的情况......
  • C++之线程管控(一)
    背景多线程编程在实际应用中非常常见,它可以帮助我们提高程序性能,实现高效的任务调度。从C++11开始,C++语言已经提供了对多线程编程的原生支持。本文将详细介绍如何使用C++进行线程管控,包括发起线程、等待线程完成、异常处理以及在后台运行线程等内容。发起线程C++11提供了一个名......
  • C++之线程管控(二)
    背景随着多核处理器的普及,多线程编程已经成为软件开发中不可或缺的一部分。C++11标准为我们带来了线程库,让我们能够更方便地在C++中实现多线程编程。在这篇博客中,我们将介绍C++线程管控的基本概念和方法,包括向线程函数传递参数,移交线程归属权,运行时选择线程数量和识别线程。向线......
  • C++ Primer 学习笔记——第七章
    第七章类前言基本数据类型有时候并不能解决某些特定问题,而通过自定义的类就可以通过理解问题概念,使得程序更加容易编写、调试和修改。类的基本思想是数据抽象(dataabstraction)和封装(encapsulation)。数据抽象是一种依赖于接口(interface)和实现(implementation)分离的编程(以及设......