C++11 cppreference.com中的std::memory\u order\u松弛示例

C++11 cppreference.com中的std::memory\u order\u松弛示例,c++11,memory-model,C++11,Memory Model,cppreference.com提供了以下使用std::memory\u order\u的示例。() #包括 #包括 #包括 #包括 std::原子cnt={0}; void f() { 对于(int n=0;n

cppreference.com提供了以下使用std::memory\u order\u的示例。()

#包括
#包括
#包括
#包括
std::原子cnt={0};
void f()
{
对于(int n=0;n<1000;++n){
cnt.fetch\u add(1,std::memory\u order\u relaxed);
}
}
int main()
{
std::向量v;
对于(int n=0;n<10;++n){
v、 后置炮台(f);
}
用于(自动&t:v){
t、 join();
}

std::cout说明的第一句话(重点是我的)中可以找到关于为什么这样做的提示:

std::memory\u order
指定如何访问内存,包括常规, 非原子内存访问将按原子顺序进行 手术

请注意,这并不是关于原子本身的内存访问,而是关于原子周围的内存访问。对单个原子的并发访问总是有严格的顺序要求,否则就不可能首先对它们的行为进行推理

在计数器的情况下,您可以保证
fetch\u add
的行为与预期非常接近:计数器一次增加一个,不跳过任何值,也不计算任何值两次。您可以通过检查单个
fetch\u add
调用的返回值来轻松验证这一点。您可以获得这些保证方法,而不考虑内存顺序

当您在周围程序逻辑的上下文中为这些计数器值赋值时,事情就会变得有趣起来。例如,您可以使用某个计数器值来指示某个特定的数据段已由先前的计算步骤提供。如果计数器和数据需要跨线程持久化:在宽松的顺序下,在观察等待的计数器值时,无法保证等待的数据也已准备就绪。即使计数器是在生成线程写入数据后设置的,内存操作的这种顺序也不会不跨线程边界进行转换。您需要指定一个内存顺序,以便根据线程间计数器的更改来对数据进行写入。 这里需要理解的关键是,虽然操作保证在一个线程内以特定顺序发生,但当从不同线程观察相同数据时,顺序不再得到保证

因此,经验法则是:如果你只是孤立地操作一个原子,你不需要任何排序。一旦在其他无关内存访问的上下文中解释了该操作(即使这些访问本身就是原子),你就需要担心使用正确的排序


通常的建议是,除非你有非常、非常、非常好的理由这样做,否则你应该坚持使用默认的
memory\u order\u seq\u cst
。作为一名应用程序开发人员,除非你有强有力的经验证据证明,内存排序值得你毫无疑问会遇到的麻烦

在描述的第一句话中可以找到关于为什么这项工作的提示(我的重点):

std::memory\u order
指定如何访问内存,包括常规, 非原子内存访问将按原子顺序进行 手术

请注意,这并不是关于原子本身的内存访问,而是关于原子周围的内存访问。对单个原子的并发访问总是有严格的顺序要求,否则就不可能首先对它们的行为进行推理

在计数器的情况下,您可以保证
fetch\u add
的行为与预期非常接近:计数器一次增加一个,不跳过任何值,也不计算任何值两次。您可以通过检查单个
fetch\u add
调用的返回值来轻松验证这一点。您可以获得这些保证方法,而不考虑内存顺序

当您在周围程序逻辑的上下文中为这些计数器值赋值时,事情就会变得有趣起来。例如,您可以使用某个计数器值来指示某个特定的数据段已由先前的计算步骤提供。如果计数器和数据需要跨线程持久化:在宽松的顺序下,在观察等待的计数器值时,无法保证等待的数据也已准备就绪。即使计数器是在生成线程写入数据后设置的,内存操作的这种顺序也不会不跨线程边界进行转换。您需要指定一个内存顺序,以便根据线程间计数器的更改来对数据进行写入。 这里需要理解的关键是,虽然操作保证在一个线程内以特定顺序发生,但当从不同线程观察相同数据时,顺序不再得到保证

因此,经验法则是:如果你只是孤立地操作一个原子,你不需要任何排序。一旦在其他无关内存访问的上下文中解释了该操作(即使这些访问本身就是原子),你就需要担心使用正确的排序


通常的建议是,除非你有非常、非常、非常好的理由这样做,否则你应该坚持使用默认的
memory\u order\u seq\u cst
。作为一名应用程序开发人员,除非你有强有力的经验证据证明,内存排序值得你毫无疑问会遇到的麻烦

是的,这是一个正确的示例-因此不,编译器可以
#include <vector>
#include <iostream>
#include <thread>
#include <atomic>
 
std::atomic<int> cnt = {0};
 
void f()
{
    for (int n = 0; n < 1000; ++n) {
        cnt.fetch_add(1, std::memory_order_relaxed);
    }
}
 
int main()
{
    std::vector<std::thread> v;
    for (int n = 0; n < 10; ++n) {
        v.emplace_back(f);
    }
    for (auto& t : v) {
        t.join();
    }
    std::cout << "Final counter value is " << cnt << '\n';
}