自旋锁与互斥量的选择

自旋锁(spin lock)与互斥量(mutex)的比较
自旋锁是一种非阻塞锁,也就是说,如果某线程需要获取自旋锁,但该锁已经被其他线程占用时,该线程不会被挂起,而是在不断的消耗CPU的时间,不停的试图获取自旋锁。
互斥量是阻塞锁,当某线程无法获取互斥量时,该线程会被直接挂起,该线程不再消耗CPU时间,当其他线程释放互斥量后,操作系统会激活那个被挂起的线程,让其投入运行。


两种锁适用于不同场景:
如果是多核处理器,如果预计线程等待锁的时间很短,短到比线程两次上下文切换时间要少的情况下,使用自旋锁是划算的。
如果是多核处理器,如果预计线程等待锁的时间较长,至少比两次线程上下文切换的时间要长,建议使用互斥量。
如果是单核处理器,一般建议不要使用自旋锁。因为,在同一时间只有一个线程是处在运行状态,那如果运行线程发现无法获取锁,只能等待解锁,但因为自身不挂起,所以那个获取到锁的线程没有办法进入运行状态,只能等到运行线程把操作系统分给它的时间片用完,才能有机会被调度。这种情况下使用自旋锁的代价很高。
如果加锁的代码经常被调用,但竞争情况很少发生时,应该优先考虑使用自旋锁,自旋锁的开销比较小,互斥量的开销较大。


参考文献
《多核程序设计技术》
《linux内核设计与实现》
from:http://blog.youkuaiyun.com/swordmanwk/article/details/6819457


pthread与tbb中各种锁的对比测试
pthread中提供的锁有:pthread_mutex_t, pthread_spinlock_t, pthread_rwlock_t。
pthread_mutex_t是互斥锁,同一瞬间只能有一个线程能够获取锁,其他线程在等待获取锁的时候会进入休眠状态。因此pthread_mutex_t消耗的CPU资源很小,但是性能不高,因为会引起线程切换。
pthread_spinlock_t是自旋锁,同一瞬间也只能有一个线程能够获取锁,不同的是,其他线程在等待获取锁的过程中并不进入睡眠状态,而是在CPU上进入“自旋”等待。自旋锁的性能很高,但是只适合对很小的代码段加锁(或短期持有的锁),自旋锁对CPU的占用相对较高。
pthread_rwlock_t是读写锁,同时可以有多个线程获得读锁,同时只允许有一个线程获得写锁。其他线程在等待锁的时候同样会进入睡眠。读写锁在互斥锁的基础上,允许多个线程“读”,在某些场景下能提高性能。
诸如pthread中的pthread_cond_t, pthread_barrier_t, semaphone等,更像是一种同步原语,不属于单纯的锁。
TBB中提供的锁有:
mutex 互斥锁,等同于pthread中的互斥锁(实际上就是对pthread_mutex_t进行封装)
recurisive_mutex 可重入的互斥锁,在pthread_mutex_t的基础上加了一个可重入的属性
spin_metux 自旋锁,与pthread_spinlock_t类似,但是性能比pthread_spinlock_t低28%
queuing_metux 公平的互斥锁,严格按照等待锁的先后顺序获得锁
spin_rw_mutex 读写自旋锁,功能与pthread_rwlock_t一致,但是性能比pthread_rwlock_t高很多
queuing_rw_mutex 公平的读写读写锁,也是严格按照等待锁的先后顺序获得锁


以下是我对一个拥有3667527个节点的HASH表进行读操作所花费的时间,可以说明各种锁的性能:
(多线程的环境为:4CPU的电脑上使用四个线程进行同样的度操作,然后取四个线程读取的平均时间)
·单线程不加锁:0.818845s
·多线程使用pthread_mutex_t:120.978713s   (很离谱吧…………我也吓了一跳)
·多线程使用pthread_rwlock_t:10.592172s   (多个线程加读锁)
·多线程使用pthread_spinlock_t:4.766012s
·多个线程使用tbb::spin_mutex:6.638609s     (从这里可以看出pthread的自旋锁比TBB的自旋锁性能高出28%)
·多个线程使用tbb::spin_rw_mutex:3.471757s (并行读的环境下,这是所有锁中性能最高的)


OK,有了以上的测试结果,何种环境该使用何种锁,不辨自明。
from:http://hi.baidu.com/ah__fu/item/5bb98aaaebf5c113a9cfb758

#include <tbb/spin_rw_mutex.h>
tbb::spin_rw_mutex g_rwMutex;
tbb::spin_rw_mutex::scoped_lock lock(g_rwMutex);

#include <boost/thread/mutex.hpp>
boost::mutex io_mutex;
boost::mutex::scoped_lock lock(io_mutex);



### 自旋锁互斥的区别 #### 等待机制 自旋锁通过忙等待(Busy-Wait)持续检查的状态,线程在等待期间保持运行状态,不释放CPU资源。互斥则在等待时主动让出CPU,进入阻塞状态,由操作系统调度唤醒[^3]。 ```c static ALWAYS_INLINE void OSSpinLockLock(volatile OSSpinLock *lock) { do { while (lock->value != 0) { // 忙等待 - 线程一直在CPU上运行 __asm__ volatile ("pause"); } } while (!OSAtomicCompareAndSwap32(0, 1, &lock->value)); } ``` #### 实现原理 自旋锁依赖原子操作(如CAS、Test-And-Set)实现,通常在用户态完成,无需内核介入。互斥则依赖操作系统提供的阻塞/唤醒机制(如信号、条件变),涉及内核态切换。 #### 性能特点 自旋锁的优点是无上下文切换开销,适合持有时间极短的场景(如几纳秒),缺点是长时间等待会浪费CPU资源。互斥的优点是等待时不占用CPU,适合持有时间较长或不可预测的场景,缺点是上下文切换可能带来较大开销[^3]。 #### 适用场景 自旋锁适用于多核系统,尤其是临界区代码极短(如内核中断处理)、线程不允许休眠的场景(如某些实时系统)以及用户态高性能同步(需结合自适应策略)。互斥适用于用户态应用程序,尤其是临界区代码较复杂或耗时较长(如文件操作)、单核CPU环境以及需要避免CPU资源浪费的场景[^3]。 #### 其他差异 自旋锁可能导致优先级反转(高优先级线程空转等待低优先级线程),而互斥通常支持优先级继承等机制解决优先级反转问题。此外,自旋锁在单核系统中需禁用中断或配合调度策略,否则可能死,而互斥适用于所有CPU架构。 #### 性能对比 自旋锁无需保存/恢复上下文,无需切换线程,响应更快,但占用CPU资源。互斥需要保存/恢复上下文,需要切换线程,响应较慢,但不占用CPU资源[^5]。 #### 使用场景示例 自旋锁适合短期、快速的操作,如计数器操作: ```c spinlock.lock(); counter++; // 非常快的操作 spinlock.unlock(); ``` 互斥适合长时间操作或可能阻塞的操作: ```c mutex.lock(); // 长时间操作或可能阻塞的操作 [self complexOperation]; mutex.unlock(); ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值