当应用程序正在编写而另一个读取时,是否需要互斥锁或信号量?

时间:2018-01-17 00:06:49

标签: architecture system shared-memory cpu-architecture multicore

有两个应用程序在不同的核心上运行。一个应用程序写入4字节的共享内存位置,另一个应用程序读取另一个应用程序。这两个应用程序同时运行。这个模型适用于我而不使用互斥锁或信号量。这有什么不足之处吗?即使在这种情况下,还需要互斥锁或信号量?

3 个答案:

答案 0 :(得分:2)

TL:DR:如果您使用std::atomic<uint32_t>加载/存储std::memory_order_relaxed进行此操作,则在C / C ++中是安全的。在所有现代建筑中,它都是安全的。

它&#34;安全&#34;所有&#34;正常&#34;没有互斥量CPU架构,但它不提供非常多的同步。由于这些限制,您只能将它用于简单的东西:

  • 如果生产者的两个商店在读取之间发生(从读者的角度来看),消费者可能会错过更新。即使消费者通常比生产者更快,读者也可能因为页面错误或上下文切换而睡眠。除非您在硬实时操作系统下运行,否则您确实可以保证最大延迟。
  • 消费者可以读取相同的值两次。如果您的数据自然不受the ABA problem的影响(生产者永远不会连续两次存储相同的值,例如一个计数器,它可以可能在读取之间回绕),那么也不是问题。

    但是如果消费者只是在等待新的值,你应该使用无锁队列(在固定大小的循环缓冲区中)。这样可以让读者在清空队列时休眠,或者如果生成器填满队列则可以阻塞。它还允许消费者处理几个项目,而生产者不必一次醒来存储它们。对于单一制作者单一消费者案例,它可以有效地实现非常,甚至对于多作者多读者案例也是如此(在这种情况下你必须阻止作家与其他作家竞争生产某种总订单。)

    您应该只查找lockless / lock-free queue的库实现,除非您确定要让读者旋转某个值,等待它更改。

通常这种只写/只读模式只能用于时间戳或&#34;当前值&#34;某事。读者不会试图查看每次更新,他们只是在需要该值时才会阅读。

在主流的现代CPU架构中,对齐的字加载/存储指令是原子的,因此您不会看到&#34;撕裂&#34; (来自两个不同商店的混合字节)。有关x86的详细信息,请参阅Why is integer assignment on a naturally aligned variable atomic on x86?

如果你在asm写作,显然你必须知道机器如何工作的细节。

在C或C ++中,您当然需要使用_Atomicstd::atomic变量,否则您的程序会有未定义的行为数据竞争。它可能会发生工作,或者共享变量的加载可能会从循环中提升。使用myvar.store(newvalu, std::memory_order_relaxed)将使您的加载/存储几乎与有效的常规整数赋值完全相同。即如果您的代码恰好只与int一起使用,那么std::atomic<int>memory_order_relaxed一起使用不应该减慢任何速度,甚至可能不会更改编译器asm输出。 (但它保证了您使用不同的周围代码或优化选项的正确性!)。

正确遵循ISO C ++ 11规则将使此生产者 - 消费者模式适用于任何符合标准的C ++实现。在一些模糊的平台上,数据竞争是硬件(不仅仅是C ++优化器)的问题,你的原子变量将使用互斥锁而不是无锁。

答案 1 :(得分:1)

这取决于平台。在不了解平台的情况下,我们甚至无法想象它会如何失败。是否需要互斥锁或信号量取决于平台的文档是否说明了它们的要求。你无法通过实验来判断 - 走在街对面而不是两个方向而且没有受到汽车的撞击并没有表明它是安全的。下一个CPU,下一个编译器,下一个系统库甚至下一次系统升级都可能导致它失败,或者它可能很少失败。

答案 2 :(得分:-1)

彼得·科德斯&#39;答案很棒。我将提供我自己的一般性答案,我认为告诉经验不足的开发人员是正确的。

是。你确实需要协调。您的应用程序似乎可以正常工作,但您没有做任何事情来确保每个值只读取一次。处理业务流程的方式取决于语言,但互斥或条件变量是有意义的。

这是一种简化,但却是安全的。在使用无锁架构进行一些工作之后,我有信心说他们已经进入了cpu细节和编译器的高级主题。