C语言中memory_order的各个级别具体如何影响多线程内存访问顺序,能否详细解释一下?

2026-04-19 02:142阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1145个文字,预计阅读时间需要5分钟。

C语言中memory_order的各个级别具体如何影响多线程内存访问顺序,能否详细解释一下?

阅读了C++并发编程实战中的内存模型部分后,对memory_order的概念仍有些模糊。今天在知云上发现了百度的brpc文档,其中有关原子操作的文档让我感觉解释得非常好。为了更深入理解,再次总结如下:

在多核处理器上,内存模型确保了多个线程之间的内存操作顺序一致性。memory_order定义了内存操作的同步级别,包括以下几种:

1. memory_order_seq_cst:提供最严格的内存顺序保证,确保所有线程都能看到相同的操作顺序。

2.memory_order_acquire:获取型内存操作,确保后续的操作不会看到之前的未同步操作。

3.memory_order_release:释放型内存操作,确保之前的操作已经同步到其他线程。

4.memory_order_acq_rel:结合了获取型和释放型,确保操作顺序的同时,也能同步操作。

5.memory_order_relaxed:不提供任何内存顺序保证,适用于性能要求高的场景。

理解这些概念对于编写高效且安全的并发程序至关重要。

看了c++并发编程实战的内存模型部分后,一直对memory_order不太懂,今天在知乎发现了百度的brpc,恰好有关于原子操作的文档,感觉解释的很好。为了加深理解,再次总结一遍。

在多核编程中,我们使用锁来避免多个线程修改同一个数据时产生的竞争条件。但是,锁会消耗系统资源,当锁成为性能瓶颈的时候,就需要使用另一种方法——原子指令。c++11中引入了原子类型atomic。

原子指令 (x均为std::atomic) 作用x.load()返回x的值。x.store(n)把x设为n,什么都不返回。x.exchange(n)把x设为n,返回设定之前的值。x.compare_exchange_strong(expected_ref, desired)若x等于expected_ref,则设为desired,返回成功;否则把最新值写入expected_ref,返回失败。x.compare_exchange_weak(expected_ref, desired)相比compare_exchange_strong可能有spurious wakeup。x.fetch_add(n), x.fetch_sub(n)原子地做x += n, x-= n,返回修改之前的值。

但仅靠原子指令实现不了对资源的访问控制。这造成的原因是编译器和cpu实施了重排指令,导致读写顺序会发生变化,只要不存在依赖,代码中后面的指令可能会被放在前面,从而先执行它。cpu这么做是为了尽量塞满每个时钟周期,在单位时间内尽量执行更多的指令,从而提高吞吐率。

下面看个例子:

// thread 1 // ready was initialized to false p.init(); ready = true;

// thread 2 if(ready) { p.bar(); }

线程2在ready为true的时候会访问p,对线程1来说,如果按照正常的执行顺序,那么p先被初始化,然后在将ready赋为true。但对多核的机器而言,情况可能有所变化:

  • 线程1中的ready = true可能会被cpu或编译器重排到p.init()的前面,从而优先执行ready = true这条指令。在线程2中,p.bar()中的一些代码可能被重排到if(ready)之前。
  • 即使没有重排,ready和p的值也会独立地同步到线程2所在核心的cache,线程2仍然可能在看到ready为true时看到未初始化的p。

为了解决这个问题,cpu和编译器提供了memory fence,让用户可以声明访存指令的可见性关系,c++11总结为以下memory order:

memory order 作用 memory_order_relaxed 无fencing作用,cpu和编译器可以重排指令 memory_order_consume 后面依赖此原子变量的访存指令勿重排至此条指令之前 memory_order_acquire 后面访存指令勿重排至此条指令之前 memory_order_release 前面的访存指令勿排到此条指令之后。当此条指令的结果被同步到其他核的cache中时,保证前面的指令也已经被同步。 memory_order_acq_rel acquare + release memory_order_seq_cst acq_rel + 所有使用seq_cst的指令有严格的全序关系

C语言中memory_order的各个级别具体如何影响多线程内存访问顺序,能否详细解释一下?

有了memoryorder,我们可以这么改上面的例子:

// Thread1 // ready was initialized to false p.init(); ready.store(true, std::memory_order_release);

// Thread2 if (ready.load(std::memory_order_acquire)) { p.bar(); }

线程2中的acquire和线程1的release配对,确保线程2在看到ready==true时能看到线程1 release之前所有的访存操作。

注意,memory fence不等于可见性,即使线程2恰好在线程1在把ready设置为true后读取了ready也不意味着它能看到true,因为同步cache是有延时的。memory fence保证的是可见性的顺序:“假如我看到了a的最新值,那么我一定也得看到b的最新值”。

到此这篇关于浅谈关于C++memory_order的理解的文章就介绍到这了,更多相关C++ memory order内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!

标签:理解看了

本文共计1145个文字,预计阅读时间需要5分钟。

C语言中memory_order的各个级别具体如何影响多线程内存访问顺序,能否详细解释一下?

阅读了C++并发编程实战中的内存模型部分后,对memory_order的概念仍有些模糊。今天在知云上发现了百度的brpc文档,其中有关原子操作的文档让我感觉解释得非常好。为了更深入理解,再次总结如下:

在多核处理器上,内存模型确保了多个线程之间的内存操作顺序一致性。memory_order定义了内存操作的同步级别,包括以下几种:

1. memory_order_seq_cst:提供最严格的内存顺序保证,确保所有线程都能看到相同的操作顺序。

2.memory_order_acquire:获取型内存操作,确保后续的操作不会看到之前的未同步操作。

3.memory_order_release:释放型内存操作,确保之前的操作已经同步到其他线程。

4.memory_order_acq_rel:结合了获取型和释放型,确保操作顺序的同时,也能同步操作。

5.memory_order_relaxed:不提供任何内存顺序保证,适用于性能要求高的场景。

理解这些概念对于编写高效且安全的并发程序至关重要。

看了c++并发编程实战的内存模型部分后,一直对memory_order不太懂,今天在知乎发现了百度的brpc,恰好有关于原子操作的文档,感觉解释的很好。为了加深理解,再次总结一遍。

在多核编程中,我们使用锁来避免多个线程修改同一个数据时产生的竞争条件。但是,锁会消耗系统资源,当锁成为性能瓶颈的时候,就需要使用另一种方法——原子指令。c++11中引入了原子类型atomic。

原子指令 (x均为std::atomic) 作用x.load()返回x的值。x.store(n)把x设为n,什么都不返回。x.exchange(n)把x设为n,返回设定之前的值。x.compare_exchange_strong(expected_ref, desired)若x等于expected_ref,则设为desired,返回成功;否则把最新值写入expected_ref,返回失败。x.compare_exchange_weak(expected_ref, desired)相比compare_exchange_strong可能有spurious wakeup。x.fetch_add(n), x.fetch_sub(n)原子地做x += n, x-= n,返回修改之前的值。

但仅靠原子指令实现不了对资源的访问控制。这造成的原因是编译器和cpu实施了重排指令,导致读写顺序会发生变化,只要不存在依赖,代码中后面的指令可能会被放在前面,从而先执行它。cpu这么做是为了尽量塞满每个时钟周期,在单位时间内尽量执行更多的指令,从而提高吞吐率。

下面看个例子:

// thread 1 // ready was initialized to false p.init(); ready = true;

// thread 2 if(ready) { p.bar(); }

线程2在ready为true的时候会访问p,对线程1来说,如果按照正常的执行顺序,那么p先被初始化,然后在将ready赋为true。但对多核的机器而言,情况可能有所变化:

  • 线程1中的ready = true可能会被cpu或编译器重排到p.init()的前面,从而优先执行ready = true这条指令。在线程2中,p.bar()中的一些代码可能被重排到if(ready)之前。
  • 即使没有重排,ready和p的值也会独立地同步到线程2所在核心的cache,线程2仍然可能在看到ready为true时看到未初始化的p。

为了解决这个问题,cpu和编译器提供了memory fence,让用户可以声明访存指令的可见性关系,c++11总结为以下memory order:

memory order 作用 memory_order_relaxed 无fencing作用,cpu和编译器可以重排指令 memory_order_consume 后面依赖此原子变量的访存指令勿重排至此条指令之前 memory_order_acquire 后面访存指令勿重排至此条指令之前 memory_order_release 前面的访存指令勿排到此条指令之后。当此条指令的结果被同步到其他核的cache中时,保证前面的指令也已经被同步。 memory_order_acq_rel acquare + release memory_order_seq_cst acq_rel + 所有使用seq_cst的指令有严格的全序关系

C语言中memory_order的各个级别具体如何影响多线程内存访问顺序,能否详细解释一下?

有了memoryorder,我们可以这么改上面的例子:

// Thread1 // ready was initialized to false p.init(); ready.store(true, std::memory_order_release);

// Thread2 if (ready.load(std::memory_order_acquire)) { p.bar(); }

线程2中的acquire和线程1的release配对,确保线程2在看到ready==true时能看到线程1 release之前所有的访存操作。

注意,memory fence不等于可见性,即使线程2恰好在线程1在把ready设置为true后读取了ready也不意味着它能看到true,因为同步cache是有延时的。memory fence保证的是可见性的顺序:“假如我看到了a的最新值,那么我一定也得看到b的最新值”。

到此这篇关于浅谈关于C++memory_order的理解的文章就介绍到这了,更多相关C++ memory order内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!

标签:理解看了