当前位置: 华文问答 > 数码

多线程编程的时候,使用无锁结构会不会比有锁结构更加快?

2016-12-14数码

具体情况需具体分析。

一部分朋友觉得用锁会影响性能,其实锁指令本身很简单,影响性能的是锁争用(Lock Contention),什么叫锁争用,就是你我都想进入临界区,但只能有一个线程能进去,这样就影响了并发度。可以去看看glibc中pthread_mutex_lock的源码实现,在没有contention的时候,就是一条CAS指令,内核都没有陷入;在contention发生的时候,就选择陷入内核然后睡觉,等待某个线程unlock后唤醒(详见Futex)。

「只有一个线程在临界区」这件事对lockfree也是成立的,只不过所有线程都可以进临界区,最后只有一个线程可以make progress,其它线程再做一遍。

所以contention在有锁和无锁编程中都是存在的,那为什么无锁有些时候会比有锁更快?他们的不同体现在拿不到锁的态度:有锁的情况就是睡觉,无锁的情况就不断spin。睡觉这个动作会陷入内核,发生context switch,这个是有开销的,但是这个开销能有多大呢,当你的临界区很小的时候,这个开销的比重就非常大。这也是为什么临界区很小的时候,换成lockfree性能通常会提高很多的原因。

再来看lockfree的spin,一般都遵循一个固定的格式:先把一个不变的值X存到某个局部变量A里,然后做一些计算,计算/生成一个新的对象,然后做一个CAS操作,判断A和X还是不是相等的,如果是,那么这次CAS就算成功了,否则再来一遍。如果上面这个loop里面「计算/生成一个新的对象」非常耗时并且contention很严重,那么lockfree性能有时会比mutex差。另外lockfree不断地spin引起的CPU同步cacheline的开销也比mutex版本的大。

lockfree的意义不在于绝对的高性能,它比mutex的优点是使用lockfree可以避免死锁/活锁,优先级翻转等问题。但是因为ABA problem、memory order等问题,使得lockfree比mutex难实现得多。

除非性能瓶颈已经确定,否则还是乖乖用mutex+condvar,等到以后出bug了就知道mutex的好了。如果一定要换lockfree,请一定要先profile,profile,profile!请确保时间花在刀刃上。