为何pthread自旋锁比互斥锁快很多?如何兼顾性能与避免自旋锁?
单生产者单消费者队列的同步性能疑问
我以一个生产者消费者示例为参考,开发了适用于单生产者、单消费者场景的多线程FIFO队列,队列仅在这两个线程间共享。测试中发现同步机制的开销差异极大:使用mutex时,完成1000万条数据的添加与移除操作耗时约3秒,而使用spinlock仅需1.5秒。
我实现的阻塞式添加函数如下(阻塞式移除函数逻辑类似),此前John Bollinger指出原自旋锁代码在多读写场景下存在竞态问题,我已对函数进行了更新:
static void spin_while_full(volatile synced_queue *me) { while (me->queue->n_elements == me->queue->capacity) { } } void synced_queue_add(synced_queue *me, void *value) { synced_queue_lock_type tp = me->lock_type; if (tp == SYNCED_QUEUE_SPIN_LOCK) { while (true) { pthread_spin_lock(&me->lock); if (me->queue->n_elements < me->queue->capacity) { break; } pthread_spin_unlock(&me->lock); } } else if (tp == SYNCED_QUEUE_SPIN_LOCK_UNCONTESTED) { spin_while_full(me); pthread_spin_lock(&me->lock); } else if (tp == SYNCED_QUEUE_MUTEX) { pthread_mutex_lock(&me->mutex); while (me->queue->n_elements == me->queue->capacity) { pthread_cond_wait(&me->var_prod, &me->mutex); } } else { assert(false); } queue_add(me->queue, value); if (tp == SYNCED_QUEUE_SPIN_LOCK || tp == SYNCED_QUEUE_SPIN_LOCK_UNCONTESTED) { pthread_spin_unlock(&me->lock); } else { pthread_mutex_unlock(&me->mutex); pthread_cond_signal(&me->var_cons); } }
库使用者可通过lock_type指定同步方式,三种锁的基准测试结果如下:
=== benchmark_synced_queue spinlock 1024 elements 0.17 us/job uncontested spinlock 1024 elements 0.14 us/job mutex 1024 elements 0.31 us/job spinlock 512 elements 0.17 us/job uncontested spinlock 512 elements 0.14 us/job mutex 512 elements 0.31 us/job spinlock 256 elements 0.15 us/job uncontested spinlock 256 elements 0.14 us/job mutex 256 elements 0.28 us/job spinlock 128 elements 0.15 us/job uncontested spinlock 128 elements 0.14 us/job mutex 128 elements 0.29 us/job spinlock 64 elements 0.16 us/job uncontested spinlock 64 elements 0.14 us/job mutex 64 elements 0.28 us/job spinlock 32 elements 0.18 us/job uncontested spinlock 32 elements 0.15 us/job mutex 32 elements 0.15 us/job spinlock 16 elements 0.21 us/job uncontested spinlock 16 elements 0.16 us/job mutex 16 elements 0.30 us/job spinlock 8 elements 0.29 us/job uncontested spinlock 8 elements 0.16 us/job mutex 8 elements 0.60 us/job spinlock 4 elements 0.43 us/job uncontested spinlock 4 elements 0.17 us/job mutex 4 elements 1.21 us/job
从结果可见,“正确实现”的自旋锁虽比存在竞态问题的自旋锁略慢,但自旋锁与mutex的性能差距更为显著。
pthread手册强烈不建议使用自旋锁:
自旋锁应结合实时调度策略(SCHED_FIFO或SCHED_RR)使用。在SCHED_OTHER等非确定性调度策略下使用自旋锁可能表明设计存在问题。……用户态自旋锁并非通用锁解决方案,本质上易出现优先级反转和无界自旋时间。使用自旋锁的程序员不仅要在代码上格外谨慎,还要注意系统配置、线程放置和优先级分配。
我的疑问是:为何mutex的锁定操作比spinlock慢这么多?自旋锁的问题在于会持续占用大量CPU资源,有没有办法不用自旋锁仍能获得相近的良好性能?
内容的提问来源于stack exchange,提问作者Changed My Name Again
相关产品推荐
相关产品推荐

