哪个更有效,基本的互斥锁或原子整数?

Mat*_*att 53 multithreading operating-system mutex pthreads atomic

对于像计数器那样简单的东西,如果多个线程将增加数量.我读到互斥锁会降低效率,因为线程必须等待.所以,对我来说,一个原子计数器将是最有效的,但我在内部读到它基本上是一个锁?所以我想我很困惑如何比另一个更有效率.

Cor*_*ica 29

如果您有一个支持原子操作的计数器,它将比互斥锁更有效.

从技术上讲,原子将在大多数平台上锁定内存总线.但是,有两个改善的细节:

  • 在内存总线锁定期间无法挂起线程,但可以在互斥锁定期间挂起线程.这就是让你获得无锁保证的原因(它没有说明没有锁定 - 它只是保证至少有一个线程可以取得进展).
  • 互斥体最终最终会被原子实现.由于您需要至少一个原子操作来锁定互斥锁,并且需要一个原子操作来解锁互斥锁,因此即使在最好的情况下,也需要至少两倍的时间来执行互斥锁.


yah*_*ahe 27

原子操作利用处理器支持(比较和交换指令)并且根本不使用锁,而锁更依赖于操作系统,并且在例如Win和Linux上执行不同.

锁实际上暂停了线程执行,为其他任务释放了cpu资源,但在停止/重新启动线程时会产生明显的上下文切换开销.相反,尝试原子操作的线程不会等待并继续尝试直到成功(所谓的忙等待),因此它们不会产生上下文切换开销,但也不会释放cpu资源.

总而言之,如果线程之间的争用足够低,通常原子操作会更快.你肯定应该做基准测试,因为没有其他可靠的方法可以知道上下文切换和忙等待之间的最低开销.

  • 我已经用谷歌搜索并阅读我的教科书好几个小时试图找到这个答案。高度,高度低估的答案 (4认同)
  • “锁实际上暂停了线程执行”,这在一般意义上是不正确的。您可以拥有自旋锁或非自旋锁。这完全取决于锁的实现方式,作为程序员,了解您正在使用哪种锁至关重要。 (4认同)

LWi*_*sey 11

最低(符合标准)互斥实现需要2个基本要素:

  • 一种原子地传递线程之间状态变化的方法('锁定'状态)
  • 内存障碍,强制执行受互斥锁保护的内存操作,以保留在受保护区域内.

由于C++标准要求的"同步"关系,你无法使它变得更简单.

最小(正确)实现可能如下所示:

class mutex {
    std::atomic<bool> flag{false};

public:
    void lock()
    {
        while (flag.exchange(true, std::memory_order_relaxed));
        std::atomic_thread_fence(std::memory_order_acquire);
    }

    void unlock()
    {
        std::atomic_thread_fence(std::memory_order_release);
        flag.store(false, std::memory_order_relaxed);
    }
};
Run Code Online (Sandbox Code Playgroud)

由于它的简单性(它不能暂停执行的线程),很可能在低争用下,这个实现优于a std::mutex.但即便如此,很容易看到受此互斥锁保护的每个整数增量都需要以下操作:

  • 一个atomic商店释放互斥锁
  • 一个atomic比较并交换(读-修改-写)来获取该互斥(可能多次)
  • 整数增量

如果将其与std::atomic<int>使用单个(无条件)读取 - 修改 - 写入(例如fetch_add)递增的独立项进行比较,则可以合理地预期原子操作(使用相同的排序模型)将优于以下情况:互斥体是用过的.


Aja*_*jay 6

Java中的原子变量类能够利用处理器提供的比较和交换指令.

以下是差异的详细说明:http: //www.ibm.com/developerworks/library/j-jtp11234/


Ron*_*TLV 5

原子整数是一个用户模式对象,因为它比在内核模式下运行的互斥锁效率更高。原子整数的范围是单个应用程序,而互斥锁的范围是计算机上所有正在运行的软件的范围。

  • 这几乎是真的。现代互斥体实现,如 Linux 的 Futex,确实倾向于利用原子操作来避免在快速路径上切换到内核模式。如果原子操作未能完成所需的任务(例如线程需要阻塞的情况),此类互斥体只需跳转到内核模式。 (2认同)