【问题标题】:Release/Acquire semantics wrt std::mutex释放/获取语义 wrt std::mutex
【发布时间】:2013-03-09 16:48:54
【问题描述】:

我正在阅读 n3485 中定义的 C++ 内存模型,它谈到了释放/获取语义,据我了解,以及 given in this blog 的定义:

获取语义是一个属性,它只能应用于从共享内存中读取的操作,无论它们是读取-修改-写入操作还是普通加载。然后将该操作视为读取-获取。 Acquire 语义防止内存重新排序 read-acquire 在程序顺序之后的任何读或写操作。

释放语义是一个属性,它只能应用于写入到共享内存的操作,无论它们是读-修改-写操作还是普通存储。然后该操作被视为写释放。释放语义防止内存重新排序写入释放与程序顺序之前的任何读取或写入操作。

将防止在当前读/写完成之前或之后重新排序读/写。第一个(获取)将确保当前正在完成的读取不会被任何读/写重新排序,后者(发布)将确保当前写入不会被之前的读/写操作重新排序它。

现在可以说std::mutex::lock 将具有acquire 语义,而std::mutex::unlock 本质上具有release 语义吗?

在标准中,我可以在部分下找到它

30.4.1.2 互斥类型 [thread.mutex.requirements.mutex]

11 同步:对同一对象的先前unlock() 操作应(1.10)此操作同步。

据我了解 synchronize with 没有在标准中明确定义,但它似乎是一种 发生在 关系的类型,查看两个语句在两个语句之间进行评估但是,根据我对获取/释放语义的理解,不同的线程更多地与内存重新排序有关。 同步也可以称为释放/获取语义?

那么释放/获取语义不仅适用于加载/存储操作的重新排序以及操作的线程内交错吗?

在关于内存模型的标准部分中,它主要讨论的是两个线程交错的有序关系。这对于这是否也适用于内存排序有待解释。

谁能澄清一下?

【问题讨论】:

  • “同步于”在 [intro.multithread], 1.10, 第 8 段中定义。
  • @PeteBecker: 是的,但那里写的内容听起来不像“定义”:“某些库调用与另一个线程执行的其他库调用同步。例如,原子store-release 与从 store (29.3) 中获取其值的 load-acquire 同步。“像“certain”和“for example”这样的词不是很正式。
  • @AndyProwl - 你是对的,它有点模糊。不是因为“确定”,这是说“一些但不是全部”的简短方式,而是因为它并没有完全说明这意味着什么。当库要求说一个操作“与”另一个操作“同步”时,这会引入一种排序,该排序成为“线程间发生之前”关系的一部分。所以没有一个地方正式定义“同步于”。这是一个分布式定义:库规范说明它发生的地方就是它发生的地方。
  • @PeteBecker:我想也许我明白你在说什么,但我不确定这是否使它成为一个“定义”:它只是说明它何时发生,而不是它是什么。现在,这样的外延定义在某些情况下可能是有意义的,但只有内涵定义才具有理解力。我可以说“Foo”是“这个人,这个人,那个人,那个人,那个人”,因为这些都是“Foo”的唯一实例。但是,用这种方式几乎不可能从“Foo”中获得意义。
  • 从概念上讲,锁定操作读取解锁互斥体的状态,解锁操作将解锁状态分配给互斥体。

标签: c++ multithreading c++11 mutex memory-model


【解决方案1】:

现在可以说 std::mutex::lock 将具有获取语义,而 std::mutex::unlock 本质上具有释放语义吗?

是的,这是正确的。

据我了解同步标准中没有明确定义

嗯,理论上第 1.10/8 段可能是为了给出 synchronizes with 的定义:

某些库调用由另一个线程执行的其他库调用同步。例如,一个 atomic store-release 从 store (29.3) 获取其值的 load-acquire 同步。 [注:...]

另一方面,这听起来不像是一个非常正式的定义。然而,第 1.10/10 段间接给出了一个更好的,虽然是隐含的:

评估 A 在评估 B 之前依赖排序如果

——A 对原子对象 M 执行释放操作,并且在另一个线程中,B 执行消费 对 M 进行操作并读取由 A 为首的释放序列中的任何副作用写入的值,或

——对于某些求值 X,A 在 X 之前是依赖排序的,并且 X 携带对 B 的依赖。

[ 注意:关系“is dependency-ordered before”类似于“synchronizes with”,但使用的是release/- 消费代替发布/获取。 — 尾注]

由于“类似于”关系通常是对称的,我会说上述“is-dependency-ordered before”的定义间接提供了一个定义的“同步”以及 - 尽管您可能正确地反对注释是非规范的;不过,这似乎是预期的定义。

我对 synchronizes with 关系的直觉是,它发生在由一个线程执行的写入(原子)操作(存储某个值)和 first(原子)之间读取该值的操作。该操作也可能在同一个线程中。

如果这两个操作在不同的线程上,那么 synchronizes-with 关系会在操作上建立跨线程排序。

在标准中,我可以在部分下找到它

30.4.1.2 互斥体类型 [thread.mutex.requirements.mutex]

11 同步:对同一对象的先前unlock() 操作应(1.10)此操作同步。

对我来说,这似乎与上面给出的解释兼容。具有释放语义(解锁、存储)的操作将与获取语义(锁定、加载)的操作同步。

但是,根据我对获取/释放语义的理解,这更多地与内存重新排序有关。同步也可以称为释放/获取语义?

释放和获取语义描述了一些操作的本质; synchronizes-with 关系(实际上)是一种关系,它以明确定义的方式在具有获取或释放语义的操作之间建立。

所以从某种意义上说,synchronizes-with 是这些操作语义的结果,我们使用这些语义来实现指令的正确排序,并限制 CPU 或编译器将执行。

【讨论】:

  • 所以互斥锁的行为很像栅栏,但唯一的区别是使用栅栏,多个线程仍然可以同时执行代码,而互斥锁使代码互斥,对吧?
  • @TonyTheLion:我仍在学习这些东西(我猜或多或少像你),我在没有太多正式知识的情况下写了这个答案,试图用我的部分理解来补充你的部分理解。但是,我认为目前我的理解不足以回答最后一个问题
  • @TonyTheLion 互斥量有序操作。 C++ 中没有“栅栏”可以实现互斥锁的功能。忘记“栅栏”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-28
  • 2013-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-25
  • 1970-01-01
相关资源
最近更新 更多