自旋锁的特点与适用场景
Linux自旋锁spinlock同一时刻只能被一个可执行线程持有。当一个线程试图获取一个已经被持有的spin lock时,就会一直忙循环-选择-等待锁重新可用。忙等待免去了线程挂起再被唤醒的转换,省去了两次上下文切换的时间。因而spinlock适合下面的场景:SMP多核系统中,持有自旋锁的时间小于完成两次上下文切换的时间,这种场景使用spinlock效率会比较高。所以一般在用户态程序中,很少会遇到使用自旋锁的场景,因为用户程序由内核调度,除非绑核一般无法保证两个需要同步的线程恰好分布在两个核上,也很少真正有持有锁时间很短少于两次上下文切换时间的。
Linux spinlock在内核中相对比较常见,因为内核中有很多需要短期加锁的场景,比如在SMP系统的中断上下文中。但是,单核CPU或者禁止内核抢占时,编译的时候自旋锁会被完全剔除出内核。这点也不难理解,单核本质上串行执行任务的,靠时间片分时执行各任务来实现并发,所以单核使用spinlock会忙等待到线程的时间片用完,这是得不偿失的。Linux spinlock不可递归,可用在中断处理程序中,(中断处理程序中不能用信号量,因为会导致睡眠)。一个注意点事:中断处理程序中处理自旋锁时,一定要在获取锁之前,关闭当前核的中断,防止在中断中又去试图获得锁而造成死锁。
spinlock在pthread中的API
/* Initialize the spinlock LOCK. If PSHARED is nonzero the spinlock can
be shared between different processes. */
extern int pthread_spin_init (pthread_spinlock_t *__lock, int __pshared)
__THROW __nonnull ((1));
/* Destroy the spinlock LOCK. */
extern int pthread_spin_destroy (pthread_spinlock_t *__lock)
__THROW __nonnull ((1));
/* Wait until spinlock LOCK is retrieved. */
extern int pthread_spin_lock (pthread_spinlock_t *__lock)
__THROWNL __nonnull ((1));
/* Try to lock spinlock LOCK. */
extern int pthread_spin_trylock (pthread_spinlock_t *__lock)
__THROWNL __nonnull ((1));
/* Release spinlock LOCK. */
extern int pthread_spin_unlock (pthread_spinlock_t *__lock)
__THROWNL __nonnull ((1));
用法跟互斥锁(互斥量)类似,这里就不举例了。总之,在用户态程序,极少有场景会适合用自旋锁。目前在实际工程中只见过涉及绑核的高性能转发有使用过,其他业务场景的同步基本靠互斥量和条件变量。