这是更有效的,基本的互斥锁或原子整数?(Which is more efficient, basic

2019-07-20 11:34发布

对于一些简单的像一个计数器,如果多个线程将增加的数量。 我读的互斥锁会降低效率,因为线程必须等待。 所以,对我来说,一个原子计数器将是最有效的,但我读了它的内部基本上是锁? 所以我想我很困惑如何既可能比其他的更有效。

Answer 1:

如果您有这原子操作支持一个计数器,它会比一个互斥体更有效。

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

  • 这是不可能暂停内存总线锁定期间线程,但它可能是一个互斥锁期间挂起线程。 这是什么让你得到一个无锁担保(不说的没有任何锁定 - 它只是保证至少有一个线程取得进展)。
  • 互斥最终结束了正与原子能实施。 既然你需要至少一个原子操作来锁定一个互斥体,和一个原子操作解锁一个互斥体,它需要至少两次长时间做一个互斥锁,即使在最好的情况下。


Answer 2:

原子操作杠杆处理器的支持(比较和交换指令),并且不使用锁可言,而锁都更依赖于操作系统和不同的执行,例如,Win和Linux操作系统。

实际上锁挂起线程执行,释放CPU资源用于其他任务,但招致明显的上下文切换停止/重新启动线程时的开销。 相反,尝试原子操作不等待和不断尝试,直到成功线程(所谓的忙等待),这样他们就不会在上下文切换开销承担,但也释放CPU资源。

综上,在一般的原子操作更快,如果线程之间的争足够低。 你绝对应该做的标杆,因为是知道什么是上下文切换和忙等待的最低开销没有其他可靠的方法。



Answer 3:

一个最小的(符合标准的)互斥的实现需要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);
    }
};

由于其简单性(它不能暂停执行的线程),它是可能的,低竞争下,这种实现优于一个std::mutex 。 但即便如此,很容易地看到,每个整数增量,通过这种互斥的保护,需要以下操作:

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

如果你比较,与一个独立std::atomic<int>是递增用单(无条件)读-修改-写(如fetch_add ),它是合理的预期,一个原子操作(使用相同的排序模型)将超越由此使用一个互斥的情况。



Answer 4:

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

这里有区别的详细描述: http://www.ibm.com/developerworks/library/j-jtp11234/



Answer 5:

原子整数是用户模式对象那里它的更有效的比它运行在内核模式互斥。 原子整数的范围是一个单一的应用程序,同时互斥的范围是在机器上所有正在运行的软件。



Answer 6:

Mutex是内核级语义甚至在提供互斥Process level 。 请注意,它可以只内(线程)的过程是在扩大跨进程互斥有帮助的,而不是。 它是昂贵的。

原子计数器, AtomicInteger用于例如,基于CAS,和平时尽量试图做操作,直到成功。 基本上,在这种情况下,螺纹种族或竞争以原子方式增加\递减值。 在这里,你可以看到正在使用一个线程试图在当前值运行良好的CPU周期。

由于要保持计数器的AtomicInteger \ AtomicLong的将是最适合你的使用情况。



Answer 7:

大多数处理器都支持原子读或写,常常原子CMP和交换。 这意味着处理器本身写入或读取单个操作的最新值,并且有可能是比较正常的整数接入失去了几个周期,尤其是在编译器不能围绕原子操作优化几乎和正常的。

在另一方面互斥是几行代码进入和离开,并在这段访问相同的位置是完全停滞的执行其它的处理器,所以显然对他们一笔大开销。 在未优化的高级代码,互斥体进入/退出和原子将函数调用,但对于互斥,任何竞争的处理器将被锁定,而你的互斥量输入函数返回,而当你的退出功能启动。 用于原子,它是唯一的,其被锁定的实际操作的持续时间。 优化应该降低成本,但不是全部。

如果你正在尝试增加,那么你的现代处理器可能支持单位递增/递减,这将是巨大的。

如果没有,则它要么使用处理器原子CMP&交换,或使用一个互斥实现。

互斥:

get the lock
read
increment
write
release the lock

原子CMP和交换:

atomic read the value
calc the increment
do{
   atomic cmpswap value, increment
   recalc the increment
}while the cmp&swap did not see the expected value

所以这第二个版本有一个循环[柜面另一个处理器增加我们的原子操作之间的值,因此值不再匹配,并且增量将是错误],可以得到长期[如果有很多的竞争对手],但总体应该还是比快互斥版本,但互斥版本可允许处理器任务切换。



文章来源: Which is more efficient, basic mutex lock or atomic integer?