我读的地方(找不到页面了)是无锁的数据结构是“某些工作负载”,这似乎在暗示,有时他们真的很慢或者从他们的增益可以在某些情况下,零更有效率。 以锁定指令做一个原子运算的〜100周期命中声音足够快,以我比去睡觉,等待调度唤醒过程备份,所以它不是明显,我在什么情况下无锁数据结构会比老式的互斥体不太可取。 如果锁可用99%的时间和过程不必去睡觉,是一个互斥那么快? 是否有拇指的认识去承担适当的无锁数据结构可用哪种方式一个好的规则?
Answer 1:
到实现无锁数据结构的常用方法是为具有可变引用不可变对象,并具有任何想要改变结构抢参考,产生施加有合适的变化对象的新版本,然后CompareExchange基准,以指向新的对象。 如果CompareExchange作品,伟大的。 如果没有,沟新对象,再抢参考,并重新开始。
这可以很好地工作,如果产生新的对象是廉价和竞争的水平足够低,使得CompareExchange通常会工作。 如果有相当大的竞争,如果产生新的对象是缓慢的,由N个线程同时尝试更新可能需要N ^ 2的时间才能完成。 作为一个极端的例子,假设100个线程被在CPU上运行,更新需要的CPU时间(仅在过去的时间片)100毫秒,和100个所有线程试图在一次更新的对象。 在最初的十秒钟,每个线程都产生在原有基础上一个新的对象。 其中一个线程将成功地做CompareExchange,而其他人都将失败。 那么接下来9.9秒期间,99个线程将生成对象,以后哪一个成功发布其更新和98将失败的新版本。 净效应将是无锁的方法将采取505秒的价值的CPU时间来执行100次更新,当锁定系统能够在大约10秒做了他们。
Answer 2:
无锁数据结构将,这种或那种方式,使用原子语义从您的架构来执行其核心业务。 当你这样做,你可以使用的机器整个内部排除机制,以确保正确排序或数据的围栏。 互斥或关键部分也做到这一点,但它只做一次为一个单一的标志。 凡互斥或关键部分是缓慢的,是当获取锁失败(有争)。 在这种情况下,OS也调用调度挂起线程,直到排除对象已被释放。
因此,它似乎是合乎逻辑,只要你锁定更少的数据结构,每个核心的方法是使用多个原子操作时,一个锁屏蔽关键部分可以提供相同的语义,有往往是非常小的争论,在实践中,对于有问题的数据结构,那么实际上,它确实更有意义使用操作系统提供的锁定机制,比试图建立自己的。
Answer 3:
我不知道使它更慢 ,但它肯定就更难得到正确的。 在许多情况下,这两种方法在性能上(或当它只是如果它需要500微微秒,而不是100微微秒无所谓)几乎相同,然后选择最简单的办法-一般lock
。
有极少数的情况下,当性能额外位是关键; 如果是 ,我怀疑你会做得很好,从建立图书馆使用预卷模式实现。 获得无锁码正常工作(并证明它正常工作在所有条件下 )通常是很辛苦。
还需要注意的是一些环境提供操作系统提供的互斥锁以上的水平; 互斥的行为,但没有一些开销(例如, Monitor
在.NET)。
Answer 4:
我想一个点添加到答案的这一部分:“凡互斥或关键部分是缓慢的,是当获取锁失败(有争)在这种情况下,OS也调用调度暂停。线程,直到排除对象已被释放“。
好像不同的操作系统可以有不同的方法,当获取锁失败怎么办。 我使用的HP-UX,它例如有一个更复杂的方法来锁定互斥。 下面是它的介绍:
...在另一方面,不断变化的背景是一个昂贵的过程。 如果等待将是短单,我们宁愿不做的上下文切换。 为了平衡这些需求,当我们试图得到一个信号,发现它锁定,我们做的第一件事是短的自旋等待。 常规psema_spin_1()被调用旋转多达50,000个时钟周期试图获得锁。 如果我们不能50,000个周期后获得锁,就可以调用psema_switch_1()放弃处理器,并让另一个进程接管。
Answer 5:
请记住,一个互斥体很可能被实现为无锁数据结构,在这个意义上,它使用一个或几个原子对象来表示它的状态。 这是一个错误的二分法。
更好的是要考虑是否需要允许多个线程等待访问一些组操作或阻塞,直到信号。 每一个需要等待的线程队列。 前者队列的线程等待访问同步区域,而后者则队列的线程等待一个信号。 Java类AbstractQueuedSynchronizer
和AbstractQueuedLongSynchronizer
提供这样的一种队列特别是CLH队列 -upon哪一个可以建立互斥,条件和其他基于队列的原语。
如果您的要求有利于,而不是只有一个线程承担一套高级的工作,而其他线程保持自由与其他工作的进行,而不是等到他们也可以做同样的工作本身,然后使用无锁技术是可能的。 是否这样做将给予更快的运行时间降低到标杆,是受到很多的频率和线程将在争夺这些同步控制,以及是否有其他的工作线程独立地执行。
Answer 6:
效率取决于度量。 锁相,或等待释放算法在系统中重要的地方抢占可以引入死锁或影响调度的最后期限。 在这种情况下,处理比正确更重要。
的OP认为锁定来替代互斥。 一些算法既不需要访问共享数据结构。 在这种情况下,无论是生产者和消费者可以同时访问相同的数据结构,而不用于其他方面。 一的示例共享队列允许单个读取器和一个写入到一个共享实例同时作用。 这符合一个用户进程可按需访问的设备驱动程序写入数据的共同需要。
进程之间的更复杂的关系可以被允许(见赫利希(1991),用于分析)用的硬件支持不同级别。 他的结论是无等待同步系统代表的是实现并发对象的传统的基于锁的技术质的突破 。
它的意思是,仍然存在一个权衡,但它不是一个简单的互斥体和自旋锁之间的选择之间。
经验法则仍然专注于正确性,而不是性能。 性能通常可以通过用钱来解决问题来实现,同时满足需求通常是更困难。