C++0x 内存模型和原子操作 (std:::atomic memory order等相关资料)

本文探讨了 C++11 中新增的并行编程特性,特别是 std::atomic 的使用及内存模型。文中详细介绍了不同 memory_order 的含义及其与内存屏障的关系,并通过示例展示了如何利用这些特性来确保数据的一致性和并发安全性。

并行编程的趋势不可阻挡啊,新的c++标准也加入了很多支持。内存模型和 std::atomic 的引入很值得学习吧,看facebook的开源c++库都用了很多c++ 11的特性了。 std::atomic 这些。


网上看到的最详细清楚的解释是,

C++ Concurrency in Action.pdf  一书的第五章

5 The C++ Memory Model and Operations on Atomic Types

对理解 c++ atomic 的memory order很有用处:


这个网页其实也是摘录的书的内容来的,

Memory Ordering for Atomic Operations in C++0x

http://www.developerfusion.com/article/138018/memory-ordering-for-atomic-operations-in


-c0x/


详细解析了 std::atomic 的不同存储模型 ,不同的值与memory order的关系:

有了Linux内核的里面内核屏障的概念的话,像下面这样里面比较容易吧。

typedef enum memory_order {

    memory_order_relaxed,   \\ 不指定内存屏障,所以内存操作执行时可能是乱序的。

    memory_order_acquire,   \\ 按照我理解就是插入一个 内存读屏障,保证之前的读操作先一步完成。清空自己cpu上"invalidate queues"  。

    memory_order_release,    \\ 按照我理解就是插入一个 内存写屏障,保证之前的写操作的顺序一定 。清空自己cpu上 的 “ store buffer"


在这之前完成

    memory_order_consume  类似memory_order_acquire,但只对依赖的指定数据起作用。

    memory_order_acq_rel,    \\ 同时插入读写两个屏障。  清空自己cpu上 的 “ store buffer" 和 "invalidate queues"  。

    memory_order_seq_cst    \\\默认的顺序模型。根据  http://en.cppreference.com/w/cpp/atomic/memory_order 这里的说明 (Total sequential ordering requires a full memory fence CPU instruction on all multi-core systems. This may become a performance bottleneck since it forces all memory accesses to propagate to every thread.)  这个会清空所有cpu上的读写同步消息队列 “ store buffer" 和 "invalidate queues"   (参考 http://hi.baidu.com/widebright/item/4e458d447c8bcfe11381daf0 ) 。  memory_order_acq_rel 应该是只清空自己cpu上 的 “ store buffer" 和 "invalidate queues"  。

        这个看上去和x86的lock指令前缀锁总线不太一样。不知道具体怎么实现的。

 

} memory_order;



书上的一个简单的例子。

-----------------------------------------------

#include <atomic>

#include <thread>

#include <assert.h>

 

std::atomic<bool> x,y;

std::atomic<int> z;

 

void write_x_then_y()

{

    x.store(true,std::memory_order_relaxed);             #1

    y.store(true,std::memory_order_release);             #2   //保证 第#1句的x写入在y的写入之前

}

 

void read_y_then_x()

{

    while(!y.load(std::memory_order_acquire));           #3     //保证 y的读取在 第#4句x的读取之前。所以  如果y看到修改后的值,肯定 第#4句看到的也是x的新的值了。

    if(x.load(std::memory_order_relaxed))                #4

        ++z;

}

 

int main()

{

    x=false;

    y=false;

    z=0;

    std::thread a(write_x_then_y);

    std::thread b(read_y_then_x);

    a.join();

    b.join();

    assert(z.load()!=0);                                 #5    //这个肯定通过的了。

}

-----------------------------------------------------------



也说道了内存屏障了

memory fences/barriers

c++ 里面除了上面那些为atomic类型指定明确的存储模型之外,也可以插入 指定的“内存读写屏障 


”的。



书中的其他章节也挺不粗哦的,

7

Designing Data Structures for

Concurrency II: Lock-free

Concurrent Data Structures



相面这个应该是c++的规范文档,解释了为什么要把c++的内存模型定义成这样子。

C++ Atomic Types and Operations

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2427.html#DiscussOrder

Memory Model Rationales

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2176.html



想std:atomic 等特性,应该在行的gcc 和 vc11 里面都有支持了,

C++11 Features in Visual C++ 11

http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx

Status of Experimental C++0x Support in GCC 4.6

http://gcc.gnu.org/gcc-4.6/cxx0x_status.html


像facebook的开源c++库好像这个std::atomic用的很多了,用来实现spinlock 。

可以去参考一下 https://github.com/facebook/folly/tree/master/folly


【转自:http://hi.baidu.com/widebright/item/ee9d66d2be106dba32db904e

C++ 中使用 `fmt::print` 输出 `std::atomic` 类型的值时,需要注意原子变量的特殊性质。以下是详细说明示例: --- ## 1. 直接输出 `std::atomic` 值 `std::atomic` 可以直接当作普通值使用 `fmt::print` 输出,因为 `load()` 操作会自动执行: ```cpp #include <atomic> #include <fmt/core.h> int main() { std::atomic<int> atomic_int{42}; fmt::print("Atomic value: {}\n", atomic_int); // 自动调用 load() return 0; } ``` **输出结果**: ``` Atomic value: 42 ``` --- ## 2. 显式调用 `load()` 输出 如果需要更明确地控制内存顺序,可以显式调用 `load()`: ```cpp #include <atomic> #include <fmt/core.h> int main() { std::atomic<int> atomic_int{42}; // 显式指定内存顺序(顺序一致性) fmt::print("Atomic value: {}\n", atomic_int.load(std::memory_order_seq_cst)); return 0; } ``` --- ## 3. 输出指针类型的 `std::atomic` 对于原子指针,`fmt::print` 同样支持直接输出: ```cpp #include <atomic> #include <fmt/core.h> int main() { int value = 42; std::atomic<int*> atomic_ptr{&value}; fmt::print("Atomic pointer: {}\n", atomic_ptr); // 输出指针地址 fmt::print("Dereferenced value: {}\n", *atomic_ptr); // 解引用输出 return 0; } ``` --- ## 4. 自定义格式化输出 如果需要特殊格式(如十六进制),可以使用 `fmt` 的格式说明符: ```cpp #include <atomic> #include <fmt/core.h> int main() { std::atomic<int> atomic_int{42}; // 十六进制输出 fmt::print("Hex: 0x{:x}\n", atomic_int.load()); // 二进制输出 fmt::print("Binary: 0b{:b}\n", atomic_int.load()); return 0; } ``` **输出结果**: ``` Hex: 0x2a Binary: 0b101010 ``` --- ## 5. 注意事项 1. **线程安全**:`fmt::print` 本身不是原子操作,但输出 `std::atomic` 的值是安全的(因为先执行了原子加载) 2. **性能**:频繁调用 `fmt::print` 输出原子变量可能影响性能(考虑批量加载) 3. **内存顺序**:默认使用 `memory_order_seq_cst`,在需要高性能的场景可以指定更宽松的内存序 --- ## 6. 完整示例 ```cpp #include <atomic> #include <thread> #include <vector> #include <fmt/core.h> std::atomic<int> counter{0}; void increment(int n) { for (int i = 0; i < n; ++i) { counter.fetch_add(1, std::memory_order_relaxed); } } int main() { constexpr int kThreads = 4; constexpr int kIncrements = 100'000; std::vector<std::thread> threads; for (int i = 0; i < kThreads; ++i) { threads.emplace_back(increment, kIncrements); } for (auto& t : threads) { t.join(); } fmt::print("Final counter: {} (expected: {})\n", counter.load(), kThreads * kIncrements); return 0; } ``` **输出结果**: ``` Final counter: 400000 (expected: 400000) ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值