微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决为什么互斥锁 (std::mutex) 很重?

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

解决方法

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

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

话虽如此,这样的想法会让您深入了解专家级并发编程。除非您有具体的问题需要担心微优化(例如滚动您自己的锁定),否则 std::mutex 是可行的方法,并且其开销在其所做的事情的合理范围内。

,

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

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

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

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

,

各种同步“重”,基于锁比原子重。

https://github.com/markwaterman/MutexShootout

此人对各种互斥体实现进行了比较。原始 Windows SWR 锁是最快的选择,但他们与之比较的最新标准互斥锁是 MSVC 2017 锁。

我相信 std::shared_mutex 是引擎盖下的 Windows SRW 锁。

您是否需要每一个微小百分比的性能?然后你应该分析和交换互斥锁。如果不是,std::mutex 与最佳选项相差不到 10%,并且可能会继续得到迭代和支持。

原子整数操作通常比互斥锁便宜,但规则更复杂。此外,原子操作会导致代码中的非本地速度变慢,因为它会导致缓存行被清除以避免其他人获得错误的值。

根据我的经验,在遇到极端情况之前,您可以进行算法更改以获得远远超过 10% 的性能变化。当你真的、真的需要性能时,你可能会尽可能多地剥离互斥锁;即使是最快的互斥锁对于真正高性能的情况也不够快。

优化是可替代的;当您发现瓶颈时,您可以花费您的开发工作来更快地编写代码。不要编写过早悲观的代码;但是在替代锁上使用 std mutex 造成的 10% 的命中率通常不足以成为该问题。

,

互斥量很昂贵,就像复制很昂贵一样。这意味着如果您可以避免复制的需要,那比必须复制要好。但如果你需要一份副本,那就没有办法了。 std::mutex 也是如此。不是因为 std::mutex 效率低下,而是因为互斥锁本身就很昂贵。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。