当前位置: 首页 > 知识库问答 >
问题:

内存Geofence:获取/加载和释放/存储

钱欣悦
2023-03-14

我对std::memory\u order\u acquire和std::memory\u order\u release的理解如下:

Acquire意味着在Acquire fence之后出现的内存访问不能重新排序到fence之前。

释放意味着在释放栅栏之前出现的任何内存访问都可以重新排序为栅栏之后。

我不明白的是,为什么在C 11原子库中,获取界限与加载操作相关联,而发布界限与存储操作相关联。

为了澄清,C 11<代码>

x.load(std::memory_order_acquire);

或者您可以使用std::memory_order_relaxed并单独指定Geofence,例如:

x.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);

我不明白的是,鉴于上述对获取和释放的定义,为什么C 11会特别将获取与加载相关联,将释放与存储相关联?是的,我已经看到了许多示例,这些示例展示了如何使用带有释放/存储的获取/加载来在线程之间进行同步,但一般来说,获取Geofence(防止语句后内存重新排序)和释放Geofence(防止语句前内存重新排序)的想法与加载和存储的想法是正交的。

例如,为什么编译器不让我说:

x.store(10, std::memory_order_acquire);

我意识到我可以通过使用memory\u order\u relaxed,然后使用一个单独的atomic\u thread\u fence(memory\u order\u acquire)语句来完成上述任务,但是,为什么我不能将store直接用于memory\u order\u acquire

一个可能的用例可能是,如果我想确保某些存储,例如x=10,发生在可能影响其他线程的其他语句执行之前。


共有3个答案

梁豪
2023-03-14

std::memory\u order\u acquirefence仅确保Geofence之后的所有加载操作在Geofence之前的任何加载操作之前都不会重新排序,因此,当执行加载之后,memory\u order\u acquire无法确保存储对其他线程可见。这就是为什么存储操作不支持memory\u order\u acquire,您可能需要memory\u order\u seq\u cst来实现存储的获取。

或者,你可以说

x.store(10, std::memory_order_releaxed);
x.load(std::memory_order_acquire);  // this introduce a data dependency

确保所有货物在商店之前没有重新订购。同样,栅栏在这里不起作用。

此外,原子操作中的内存顺序可能比内存Geofence便宜,因为它只保证相对于原子指令的顺序,而不是Geofence前后的所有指令。

有关详细信息,请参见正式描述和解释。

养翔
2023-03-14
匿名用户

(部分回答纠正了问题早期的一个错误。大卫·施瓦茨的回答已经很好地涵盖了你要问的主要问题。杰夫·普雷欣关于获取/发布的文章也是一篇很好的阅读材料,可以让你重新理解它。)

您对获取/释放的定义对于Geofence是错误的;它们仅适用于获取操作和释放操作,如x.store(mo\u release),而不适用于std::atomic\u thread\u fence(mo\u release)。

>

  • Acquire意味着在Acquire fence之后出现的内存访问不能重新排序到fence之前。[错误,对于获取操作是正确的]

    释放意味着释放界限之前出现的内存访问不能重新排序到界限之后。[错误,释放操作正确]

    对于fences来说,它们是不够的,这就是为什么ISO C对获取fences(阻止LoadStore/LoadLoad重新排序)和释放fences(LoadStore/StoreStore)有更强的排序规则。

    当然,ISO C没有定义“重新排序”,这意味着您正在访问某个全局相干状态。改为ISO C

    Jeff Preshing的文章与此相关:

    • 获取和释放语义(获取/释放操作,如加载、存储和RMW)
    • 获取和释放Geofence的工作方式与您预期的不同,这解释了为什么这些单向屏障定义对于Geofence来说是不正确和不充分的,这与操作不同。(因为它会让Geofence一直重新排序到程序的一端,并使所有操作彼此无序,因为它不与操作本身相关联。)

    如果我想确保在执行可能影响其他线程的其他语句之前发生一些存储,比如x=10,那么这可能是一个可能的用例。

    如果“其他语句”是从原子共享变量加载的,那么实际上需要std::memory\u order\u seq\u cst来避免StoreLoad重新排序<代码>获取/释放/acq\U rel不会阻止该操作。

    如果您的意思是确保原子存储在其他原子存储之前可见,通常的方法是使第二个原子存储使用mo_release

    如果第二个存储不是原子的,那么任何读卡器都不可能安全地与任何东西同步,从而在没有数据竞争的情况下观察到值。

    (虽然当破解使用普通非原子对象作为有效负载的SeqLock时,您确实会遇到发布Geofence的用例,以允许编译器进行优化。但这是一种特定于实现的行为,取决于了解std::atomic的东西如何为实际CPU编译。例如,请参阅使用32位原子实现64位原子计数器。)

  • 汪鸿志
    2023-03-14

    假设我写了一些数据,然后我写了一个指示,表明数据现在已经准备好了。其他线程看到数据已准备就绪的指示时,必须看到数据本身的写入。因此,之前的写入无法超过该写入。

    假设我读到一些数据已经准备好了。在看到数据准备就绪后,我发出的任何读取都必须在看到数据准备就绪后进行。因此后续读取不能移到该读取之后。

    因此,当您进行同步写入时,您通常需要确保您之前进行的所有写入对任何看到同步写入的人都是可见的。当您进行同步读取时,通常必须在同步读取之后进行任何读取。

    或者,换句话说,获取通常是您可以获取或访问资源的读取,后续的读取和写入不得在它之前移动。发布通常是写入您已完成对资源的使用,之前的写入不得移动到它之后。

     类似资料:
    • 问题内容: 我应该在将分配的字符串传递给之后释放它吗? 我有一些类似的代码: 在将字符串传递给之后释放字符串时,出现错误。如果我删除呼叫,该错误消失。我究竟做错了什么? 我看到矛盾的意见。有人说我应该自己释放它,有人说VM释放它,有人说VM不释放它,而您应该用奇怪的巫术魔术来释放它。我很困惑。 问题答案: 参数to 的存储完全由您负责:如果您分配了,则需要它。因此,您发布的代码段是正确的。您正在其

    • 问题内容: 在以下示例中,我有一些有关内存使用的相关问题。 如果我在解释器中运行, 我的机器上使用的实际内存最高为80.9mb。那我 实际内存下降,但仅限于。解释器使用基线,因此不向 释放内存有什么好处?是否因为Python正在“提前计划”,以为你可能会再次使用那么多的内存? 它为什么特别释放- 释放的量基于什么? 有没有一种方法可以强制Python释放所有已使用的内存(如果你知道不会再使用那么多

    • 我有一个问题,我的GPU内存是没有释放后关闭在Python中的tenstorflow会话。这三行足以导致问题: 在第三行之后,内存不会被释放。我在很多论坛上走来走去,尝试了各种各样的建议,但都没有效果。有关详细信息,请参见下面我的评论: https://github.com/tensorflow/tensorflow/issues/19731 在这里,我已经记录了我设法杀死进程并释放内存的方法,但

    • 我改进了代码,以便从垃圾收集器中获得更好的结果。 现在,当我调用时,它确实释放了所有内存。但是,当我在不调用 的情况下观察内存使用情况时,应用程序确实会保留并使用越来越多的内存。 这是否意味着我的改进正在起作用,我的所有引用都是正确的,我可以忽略JVM是如何自己释放内存的。或者,我的代码中是否存在其他问题,这些问题是JVM在不运行垃圾收集器的情况下保留更多内存的原因。

    • 我有一个Java堆转储,我想了解使用的堆的大小和可用的空闲内存。我正在使用Eclipse内存分析器。这可能吗?

    • 问题内容: 所以我有这个C ++程序,它是通过Java程序中的JNI调用的,代码如下: 在倒数第二行中,从不释放而是返回,是否会导致最终的内存泄漏?反正有解决这个问题的方法吗? 还有可能不是返回字符串而是返回布尔值(由LogonUser函数返回),而不是jstring,而是添加了要在方法中传递的“ errormessage”引用,并更新了它?我的Java程序能否看到“ errormessage”的