为什么互斥锁(std::mutex)很重?

在这个网站上,我经常在其他论坛上读到诸如“互斥量很重,最好使用其他东西”之类的短语。但我真的找不到解释为什么它很重?此外,如果我们在 C++20 之前谈论标准 C++11,我们基本上只有 std::mutex,与锁或 condition_variable 一起使用,以实现线程安全,我希望 std 中的某些东西非常有效,尤其是如果它是执行某些任务的唯一工具(在 C++20 之前),则在这种情况下是线程安全的。那么为什么互斥体,尤其是 std::mutex 很重呢?我们作为 C++ 开发人员应该使用什么?来自提升的东西?

回答

互斥体被认为是“重的”,因为它们通常被认为会导致系统调用,即到内核的往返。由于特权代码和非特权代码之间的上下文切换,内核之旅需要 1,000 多个 CPU 周期。

如今,在许多操作系统中,互斥锁被优化为在发生争用之前不会进入内核。例如,在 Linux 中它是使用 futex(“快速用户空间互斥锁”)实现的,在 Windows 中 - SRW 锁。但是,一旦发生争用,就会有内核的行程。一旦一个线程需要等待,它就会被操作系统“置于睡眠状态”,并且在释放锁的那一刻和该线程被安排再次执行的时间之间会有很大的延迟。

如果您需要同步,有时循环一个简单的atomic就足够了。如果争用很少而且很短,那么您可以使用“自旋锁”实现更好的性能,即循环直到满足特定条件。即使循环 10000 次,它也可以比单个系统调用更快。

然而,在实践中,互斥体将在性能和便利性之间提供足够的平衡。因此,除非您计算纳秒(如在 HFT 或实时应用程序中),否则我不会担心它。


回答

std::mutex被设计为围绕操作系统的本机互斥工具的轻量级便携式包装器。如果您的目标是调用这些工具,mutex与直接调用 OS 原生 API 相比,只会引入非常微不足道的开销。

但是,根据您的用例是什么,使用操作系统工具可能不是最佳解决方案。例如,为了保护数据免受并发访问,您还可以从较低级别的原语(如std::atomic. 然而,这将是一种不同的锁定算法。特别是,std::mutex如果不能立即获得互斥锁,将使等待线程进入睡眠状态,这是您在不与操作系统交谈的情况下无法做到的。但在某些情况下,这种更简单的锁定算法足以完成工作。这里的一个流行示例是锁争用预计仅在极少数情况下发生的情况。

话虽如此,这样的想法会让您深入了解专家级并发编程。除非您有特定的顾虑需要担心微优化,例如滚动您自己的锁定,否则std::mutex是要走的路,并且它的开销在合理的范围内进行。


以上是为什么互斥锁(std::mutex)很重?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>