浅析C++ atomic 和 memory ordering

浅析C++ atomic 和 memory ordering

简介

C++11 中引入了一个新的原子类型 —— std::atomic,用以在多线程环境中实现原子操作。同时,它也提供了 Memory Ordering 来确保原子操作的顺序性。本文将从理论和实践角度浅析 C++ atomic 和 memory ordering。

原子操作

原子操作是指一个操作要么全部完成,要么全部不完成,没有中间状态。例如对于下面的代码:

int i = 0;
i++;

在单线程环境中,这个代码很简单,i 的值将会被增加 1。但是在多线程环境中,如果两个线程同时执行上述代码中的 i++ 操作,由于没有对 i 进行原子化保护,最终 i 的值可能会小于预期的值。

C++11 中提供了 std::atomic 的数据类型,用以实现原子操作。std::atomic 是一种特殊的数据类型,它提供了一些函数来实现原子化操作,确保它们是以原子方式进行的。例如:

#include <atomic>

std::atomic<int> i(0);
i++;

上述代码实现了对 i 的原子增加操作,任意两个线程并发执行该操作也不会导致结果不正确。

除了原子化操作之外,std::atomic 还支持 Load 和 Store 操作。Load 操作从内存中读取数据,而 Store 操作则写入数据。

std::atomic<int> i(0);
int j = i.load();
i.store(1);

在上述代码中,i.load() 会将当前存储在 i 中的值读入 j 中,而 i.store(1) 则会将 1 写入 i 中。

内存顺序

在多线程环境中,原子操作的顺序性是需要被保证的。为了保证原子操作的顺序性,C++11 提供了 Memory Ordering 的机制,即内存顺序。

内存顺序是指代码中各个线程对 Memory Ordering 的要求,以及在应用程序中对信息进行同步的一个机制。

std::memory_order 是一个枚举类型,它提供了多种内存序,如 std::memory_order_relaxedstd::memory_order_seq_cst 等。

relaxed 内存序

std::memory_order_relaxed 表示没有任何顺序约束,可以使用最松散的约束来实现最高的性能。但是需要注意的是,使用 relaxed 内存序可能导致编写的代码在不同线程中表现出不同的行为。

下面是一个例子:

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

using namespace std;

atomic<int> x(0), y(0);
int r1 = 0, r2 = 0;

void write_x()
{
    x.store(1, memory_order_relaxed);
    r1 = y.load(memory_order_relaxed);
}

void write_y()
{
    y.store(1, memory_order_relaxed);
    r2 = x.load(memory_order_relaxed);
}

int main()
{
    thread a(write_x);
    thread b(write_y);
    a.join();
    b.join();
    cout << "r1: " << r1 << ", r2: " << r2 << endl;
    return 0;
}

在该例子中,线程 a 中的 x.store(1, memory_order_relaxed) 和线程 b 中的 y.store(1, memory_order_relaxed) 在执行时都是用 relaxed 内存序进行的。因此,二者之间的顺序在不同的运行环境中是无法保证的,即可能先执行 a,也可能先执行 b,因而 r1 和 r2 的值都有可能输出 0,都有可能输出 1,还可能输出 0 和 1。

release-acquire 内存序

std::memory_order_releasestd::memory_order_acquire 是针对 release 和 acquire 操作的 Memory Ordering 类型。

release 操作用于将本线程内存中的一个值写回到主存中,并发出信号禁止 CPU 将这些 write 操作放入到 CPU 内存队列中。它与 acquire 操作组合起来,能够实现同步传递的过程。

acquire 操作用于将内存从主存中读入到 CPU 内部缓存中,并等待前面的 release 操作完成后才进行操作。

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

using namespace std;

atomic<int> x;
atomic<int> y;

void write_x()
{
    x.store(1, std::memory_order_release);
}

void write_y()
{
    while (y.load(std::memory_order_acquire) != 1);
    cout << "y is 1" << endl;
}

int main()
{
    thread a(write_x);
    thread b(write_y);
    a.join();
    b.join();
    return 0;
}

在该例子中,线程 a 首先执行了 x.store(1, std::memory_order_release) 来写入 x 的值,该操作使用 release 内存序。线程 b 执行了 y.load(std::memory_order_acquire) 来读取 y 的值,当 y 的值为 1 时,程序输出 "y is 1"。该操作使用 acquire 内存序。

通过 release 和 acquire 操作,线程 b 可以正确地读取到线程 a 中写入的 x 的值,并做出相应的行动。

结论

C++11 提供了原子类型 std::atomic 来确保在多线程环境中的原子操作,并提供了内存顺序机制来保证原子操作的顺序性。在使用时需要根据具体情况进行选择。

参考资料

  1. C++11 标准 - 29. Lock-free property
  2. C++11并发编程:原子操作和内存序

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅析C++ atomic 和 memory ordering - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • python中的decimal类型转换实例详解

    下面就为大家详细讲解“Python中的decimal类型转换实例详解”的完整攻略。 概述 Python中的decimal类型是用于精确计算的浮点数,可以解决常规浮点数运算产生的误差问题。而在进行decimal类型的转换过程中,需要注意其精度和舍入模式等因素。 基本用法 创建decimal类型 要创建decimal类型,需要调用decimal模块中的Decim…

    C 2023年5月22日
    00
  • 用C语言操作MySQL数据库的通用方法

    使用C语言操作MySQL数据库,需要借助MySQL提供的C API。下面将介绍MySQL数据库的C API使用的基本步骤和示例代码。 步骤 引入MySQL连接库头文件 在代码中引入MySQL连接库的头文件:#include <mysql.h> 初始化数据库连接 在代码中使用mysql_init()函数初始化一个MYSQL对象,并使用mysql_r…

    C 2023年5月22日
    00
  • C++实现“隐藏实现,开放接口”的方案

    “隐藏实现,开放接口”是一种基于面向对象设计理念的编程思想,可以通过C++语言的特性来实现。下面是如何使用C++实现“隐藏实现,开放接口”的方案攻略。 封装类的实现 封装是实现隐藏实现的核心。我们使用类来封装相关的数据和函数,并将其对外部隐藏,只提供接口给外部访问。下面是一个简单的封装类的例子: class Point { public: Point(int…

    C 2023年5月23日
    00
  • 提升编程能力的C语言技巧总结

    提升编程能力的C语言技巧总结 提升编程能力的C语言技巧总结主要包括以下几个方面: 1. 深入理解指针的概念和用法 指针是C语言的重要概念之一,深入理解指针的概念和用法有助于提升编程能力。下面是两个指针的示例。 示例1:指针作为函数参数 void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp;…

    C 2023年5月23日
    00
  • 天谕雷罡圣堂怎么加点 天谕雷罡圣堂加点攻略

    天谕雷罡圣堂加点攻略 天谕雷罡圣堂是一款策略RPG游戏,在游戏中加点是非常重要的一件事情。本文将为大家介绍如何正确地加点以及天谕雷罡圣堂加点攻略。 加点原则 根据职业特长加点,如攻击型职业加攻击,防御型职业加防御等; 根据职业技能加点,如有狂暴技能的职业需要加点提升狂暴效果等; 根据自己的游戏风格加点,如喜欢输出的可以加攻击,喜欢坦克的可以加防御等; 根据B…

    C 2023年5月22日
    00
  • Go语言JSON解析器gjson使用方法详解

    Go语言JSON解析器gjson使用方法详解 在Go语言中有一个非常实用的JSON解析器库gjson,它支持在JSON文本中进行高效的路径查询和解码,操作简单,性能优秀。本文将详细讲解gjson的基本使用方法,让大家能够更方便地使用这个强大的工具。 安装gjson gjson使用起来非常简单,只需安装: go get github.com/tidwall/g…

    C 2023年5月23日
    00
  • C语言实现走迷宫

    当我们想要C语言实现走迷宫时,我们需要考虑以下步骤: 定义迷宫的数据结构与迷宫的初始化。 使用DFS或BFS等算法遍历迷宫。 处理搜索的结果,输出路径或者其他信息。 下面我将详细解释如何实现这些步骤。 定义迷宫的数据结构与迷宫的初始化 迷宫的数据结构通常使用二维字符数组来表示,其中每个位置包含一个字符表示当前位置的状态。我们可以使用常见的“#”代表障碍物,使…

    C 2023年5月23日
    00
  • 深入理解C/C++混合编程

    深入理解C/C++混合编程 混合编程是指在C/C++程序中调用其他语言编写的模块,例如Python、Java等。在C/C++中实现混合编程有多种方法,本文将详细讲解其中的两种常见方法。 方法1:使用C++的Extern-C机制 C++支持Extern-C机制,能够将C++代码转化为类似于C的代码,从而实现C/C++混合编程。具体操作步骤如下: 使用C++编写…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部