C++11标准原子库内存顺序memory_order_consume
与memory_order_acquire
的差异示例
贺志国
在C++11标准原子库中,大多数函数接收一个memory_order参数:
enum memory_order {memory_order_relaxed,memory_order_consume,memory_order_acquire,memory_order_release,memory_order_acq_rel,memory_order_seq_cst
};
上述枚举变量虽然有六个选项,但仅代表三种内存模型:
(1)顺序一致性(sequentially consistent
,即memory_order_seq_cst
)
(2)获取-释放序(memory_order_consume
, memory_order_acquire
, memory_order_release
、memory_order_acq_rel)
(3)自由序(或者叫松弛充,memory_order_relaxed
)。
除非为特定的操作指定一个内在顺序选项,内存顺序默认都是memory_order_seq_cst
。
memory_order_consume
和memory_order_acquire
都为了同一个目的:帮助非原子信息在线程间安全的传递。就像获取操作(memory_order_acquire
)一样,消费操作(memory_order_consume
)必须与另一个线程的释放操作(memory_order_acquire
)一起使用。它们的差异在于:memory_order_acquire
保证配对使用的 memory_order_release
操作之前的所有原子和非原子的写入操作有效,但memory_order_consume
仅保证配对使用的 memory_order_release
操作之前的所有原子的写入操作有效。
下面使用一个示例来说明:
示例代码如下:
#include <atomic>
#include <cassert>
#include <iostream>
#include <thread>int a;
std::atomic<bool> flag;void Write() {a = 1;flag.store(true, std::memory_order_release);
}void Read() {// std::memory_order_acquire保证// flag.store(true, std::memory_order_release);// 之前的a = 1; 一定会先执行,但// std::memory_order_consume,std::memory_order_relaxed// 无法保证这点// 注意:// X86平台上无内存顺序的差别,任何内存顺序断言都会成功// 但在ARM平台上,如果使用// std::memory_order_consume,std::memory_order_relaxed,// 断言可能失败。while (!flag.load(std::memory_order_acquire)) {std::this_thread::yield();}std::cout << "a must be 1.\n";assert(1 == a);
}int main() {a = 0;flag = false;std::thread write_thread(Write);std::thread read_thread(Read);write_thread.join();read_thread.join();return 0;
}
CMake构建文件如下:
cmake_minimum_required(VERSION 3.0.0)
project(memory_order VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 14)add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp)find_package(Threads REQUIRED)
target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})include(CTest)
enable_testing()
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
运行结果如下:
./memory_order
a must be 1.
注意:memory_order_consume
这个内存序非常特殊,主流编译器直接将它当成memory_order_acquire
处理。“C++ Concurrency In Action”(第二版,2019)一书作者认为memory_order_consume
不应该出现在实际的代码中,即使在C++17中也不推荐使用。