我需要为结构的某些成员提供同步 如果结构是这样的
struct SharedStruct {
int Value1;
int Value2;
}
我有一个全局变量
SharedStruct obj;
我希望从处理器写入
obj.Value1 = 5; // Processor B
让其他处理器立即可见,这样当我测试值
时 if(obj.Value1 == 5) { DoSmth(); } // Processor A
else DoSmthElse();
获取新值,而不是缓存中的旧值
首先,我认为如果我在写/读取值时使用 volatile ,那就足够了。但我读到,挥发性无法解决这类问题
保证成员在2/4/8字节边界上正确对齐,在这种情况下写入应该是原子的,但我不确定缓存如何干扰这一点。
使用记忆障碍(mfence,sfence等)就够了吗?或者需要一些互锁操作?
或者类似
lock mov addr, REGISTER
?
最简单的显然是一些锁定机制,但速度是至关重要的,无法承受锁:(
修改
也许我应该澄清一下。该值仅设置一次(表现得像一个标志)。所有其他线程只需阅读它。这就是为什么我认为它可能是一种在不使用锁的情况下强制读取这个新值的方法。
提前致谢!
答案 0 :(得分:7)
没有免费午餐这样的事情。如果从多个线程访问您的数据,并且必须通过其他线程立即看到更新,那么您必须通过互斥锁,读取器/写入器锁或某种类似的机制保护共享结构。
在同步代码时,性能是一个有效的问题,但它的正确性胜过它。一般来说,首先瞄准正确性,然后分析您的代码。当你尚未确定正确性时担心性能是不成熟的优化。
答案 1 :(得分:3)
这里的所有其他答案似乎都在挥手解决使用互斥锁等更新共享变量的复杂性。确实,您希望更新是原子的。 你可以使用各种操作系统原语来确保这一点,这样做会很好 编程风格。
然而,在大多数现代处理器(当然是x86)上,由于高速缓存一致性,小的,对齐的标量值的写入是原子的并且对于其他处理器立即可见。 所以在这种特殊情况下,你不需要所有的同步垃圾;硬件做了 原子操作给你。当然,这对于4字节值是安全的(例如,32位C编译器中的“int”)。
因此,您可以在启动并行线程之前使用不感兴趣的值(比如0)初始化Value1,并在那里简单地写入其他值。如果问题是以固定值退出循环(例如,如果value1 == 5),这将是完全安全的。
如果您坚持捕获第一个值,那么这将无效。但是如果你有一组并行的线程,并且任何值写的除了不感兴趣的那个之外,这也没关系。
答案 2 :(得分:2)
我是第二个peterb的答案,首先瞄准正确性。是的,你可以在这里使用记忆障碍,但它们不会做你想要的。
你立刻说 。但是,此更新可以立即执行,您可以(并且将会)最终执行if()
子句,然后设置标志,然后再执行DoSmthElse()
。这被称为竞争条件......
您希望同步某些内容,但似乎 此标志。
答案 3 :(得分:2)
明确使用原子指令。我相信大多数编译器都将这些作为内在函数提供。比较和交换是另一个好的。
如果您打算编写无锁算法,则需要编写它以便只有在条件符合预期时才会生效。
例如,如果您打算插入链接列表对象,请使用比较/交换内容,以便只有在实际执行更新时指针仍指向同一位置时才会插入。
或者如果您要减少引用计数并释放计数0的内存,您将需要通过使其以某种方式不可用来预先释放它,检查计数是否仍为0然后真正释放它。或类似的东西。
使用锁,操作,解锁设计通常要容易得多。无锁算法非常困难才能做到正确。
答案 4 :(得分:0)
使字段变为volatile会使更改在其他线程中“立即”显示,但不能保证在线程B测试值之后但在线程B执行之前不会发生线程A执行更新的瞬间if / else语句的主体。
听起来你真正想做的就是将if / else语句设为原子,这需要一个锁或一个能够容忍这种情况的算法。