【问题标题】:synchronizes-with, happens-before relation and acquire-release semanticssynchronizes-with、happens-before 关系和获取释放语义
【发布时间】:2011-05-25 06:36:49
【问题描述】:

我需要帮助来理解同步关系。我越读它试图理解的例子,我就越觉得我什么都不懂。有时我觉得这就是它,我明白了,但在看了另一个例子之后,我再次感到困惑。请帮我把它弄好。

据说操作A与操作B同步-如果A是对某个原子变量m的存储,具有释放语义,B是来自同一个变量m的加载,具有获取语义,并且B读取存储的值由 A. 也有人说,操作 A 发生在操作 B 之前,如果

  • A 在与 B 相同的线程上执行,并且 A 在程序顺序上位于 B 之前,或者
  • A 与 B 同步,或
  • A 发生在其他操作 C 之前,C 发生在 B 之前

好的。如果我们看这个例子

thread0 执行 | thread1 执行


存储 x(发布)|加载 x(获取)

这里存储到 x 是否与来自 x 的加载同步?如果我们在这里确实有同步关系,那么存储到 x 发生在从 x 加载之前,所以在线程 0 中存储到 x 之前排序的所有内容都发生在从线程 1 中从 x 加载之前。这意味着这里有强制排序。这样对吗?但在这种情况下,我不明白定义部分的“和 B 读取 A 存储的值”是什么意思?如果线程 1 比线程 0 更快,它可能会读取旧值。那么这里的关系是什么,有什么关系吗?如果没有,我该如何提供这种关系?

提前致谢。

【问题讨论】:

标签: language-agnostic synchronization atomic memory-barriers


【解决方案1】:

我不能说我非常熟悉这些术语,但我认为它是这样的。我将使用 .NET 定义的术语:“如果其他处理器总是在任何后续操作的效果之前看到它的效果,则该操作具有获取语义。如果其他处理器将在效果之前看到每个先前操作的效果,则操作具有释放语义操作本身。"

在示例中,存储和加载之间没有强制排序。任何一个都可以先执行。当存储操作恰好在加载之前执行时,A 与 B 同步。发生这种情况时,保证存储(释放)之前的所有操作在加载(获取)之后的操作执行之前执行。

但是,加载操作可能会在存储之前执行。在这种情况下,A 不会与 B 同步(因为“并且 B 读取 A 存储的值”的条件不成立),因此加载之后的操作可能会在存储之前的操作之前执行。顺序很模糊。

释放语义保证对于 x 的某个值,我们将知道存储之前的操作将在我们能够在第二个线程中加载相同的存储值之前执行(并且在我们能够执行以下操作之前)负载)。

让我们假设count和flag被初始化为零并且两个线程并行运行:

thread0:
st          count, 1   (A)
st.release  flag, 1    (B)

thread1:
ld.acquire  flag       (C)
ld          count      (D)

我们知道 A 发生在 B 和 C 发生在 D 之前,因为它们的顺序是由释放和获取语义强制的。 B 和 C 的顺序是不确定的。它只有在 B 与 C 同步时才被定义,然后我们知道 A 发生在 D 之前(就像 A 发生在 B 发生之前 C 发生在 D 之前)。

在线程 1 中,如果 flag 为 1,则计数始终为 1。如果 flag 为 0,则计数可能为 0 或 1。我们可以测试 flag 以确定其他线程是否已将值设置为计数。

如果没有获取和释放语义,加载和存储可以重新排序,并且标志和计数都可以是 0 或 1,彼此不依赖。

如果我们想确保 B 在 C 之前发生,我们可以使用信号量或其他一些等待和信号机制。在前面的示例中,我们可以通过忙于等待设置标志来强制执行订单。

thread1:
ld.acquire  flag       (C)
repeat C while flag == 0
ld          count      (D)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-05
    • 1970-01-01
    • 2014-02-04
    • 1970-01-01
    • 2019-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多