12

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

据说操作 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 更快,它可能会读取旧值。那么这里的关系是什么,有什么关系吗?如果没有,我该如何提供这种关系?

提前致谢。

4

1 回答 1

9

我不能说我很熟悉这些术语,但我认为它是这样的。我将使用 .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。我们可以测试标志以确定其他线程是否已将值设置为计数。

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

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

thread1:
ld.acquire  flag       (C)
repeat C while flag == 0
ld          count      (D)
于 2011-01-14T17:07:54.850 回答