自旋锁和互斥锁的区别

自旋锁和互斥锁的区别

POSIX threads(简称Pthreads)是在多核平台上进行并行编程的一套API。线程同步是并行编程中非常重要的通讯手段,其中最典型的应用就是用

Pthreads提供的锁机制(lock)来对多个线程之间的共享临界区(Critical Section)进行保护(另一种常用的同步机制是barrier)。

Pthreads提供了多种锁机制:

  • Mutex(互斥量):pthread_mutex_t
  • Spin lock(自旋锁): pthread_spin_t
  • Condition Variable(条件变量): pthread_cond_t
  • Read/Write lock(读写锁):pthread_rwlock_t

Pthreads提供的Mutex锁操作相关的API主要有:

  • pthread_mutex_lock(pthread_mutex_t *mutex);
  • pthread_mutex_trylock(pthread_mutex_t *mutex);
  • pthread_mutex_unlock(pthread_mutex_t *mutex);

Pthreads提供的Spin Lock锁操作相关的API主要有:

  • pthread_spin_lock(pthread_spinlock_t *lock);
  • pthread_spin_trylock(pthread_spinlock_t *lock);
  • pthread_spin_unlock(pthread_spinlock_t *lock);

从实现原理上来讲,Mutex(互斥锁)属于sleep-waiting类型的锁。例如在一个双核的机器上有两个线程(线程A和线程B),它们分别运行在Core0和Core1上。假设线程A想要通过pthread_mutex_lock操作去得到一个临界区的锁,而此时这个锁正被线程B所持有,那么线程A就会被阻塞,

Core0会在此时进行上下文切换(Context Switch)将线程A置于等待队列中,此时Core0就可以运行其它的任务而不必进行忙等待。而Spin lock(自旋锁)则不然,它属于busy-waiting类型的锁,如果线程A是使用pthread_spin_lock操作去请求锁,那么线程A就会一直在Core0上进行忙等待并不停的进行锁请求,直到得到这个锁为止。

自旋锁(Spin lock)

自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,“自旋锁”的作用是为了解决某项资源的互斥使用。因为自旋锁不会引起调用者睡眠,所以自旋锁的效率远高于互斥锁。

自旋锁的不足之处:

自旋锁一直占用着CPU,他在未获得锁的情况下,一直运行(自旋),所以占用着CPU,如果不能在很短的时间内获得锁,这无疑会使CPU效率降低。

在用自旋锁时有可能造成死锁,当递归调用时有可能造成死锁,调用有些其他函数也可能造成死锁,如 copy_to_user()、copy_from_user()、kmalloc()等。

因此我们要慎重使用自旋锁,自旋锁只有在内核可抢占式或SMP的情况下才真正需要,在单CPU且不可抢占式的内核下,自旋锁的操作为空操作。自旋锁适用于锁使用者保持锁时间比较短的情况下。

 

 

自旋锁-原理

 

互斥锁一样,一个执行单元要想访问被自旋锁保护的共享资源,必须先得到锁,在访问完共享资源后,必须释放锁。如果在获取自旋锁时,没有任何执行单元保持该锁,那么将立即得到锁;如果在获取自旋锁时锁已经有保持者,那么获取锁操作将自旋在那里,直到该自旋锁的保持者释放了锁。由此我们可以看出,自旋锁是一种比较低级的保护数据结构或代码片段的原始方式,这种锁可能存在两个问题:

1、死锁试图递归地获得自旋锁必然会引起死锁:递归程序的持有实例在第二个实例循环,以试图获得相同自旋锁时,不会释放此自旋锁。

在递归程序中使用自旋锁应遵守下列策略:

递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。此外如果一个进程已经将资源锁定,那么,即使其它申请这个资源的进程不停地疯狂"自旋",也无法获得资源,从而进入死循环。

2、过多占用cpu资源。如果不加限制,由于申请者一直在循环等待,因此自旋锁在锁定的时候,如果不成功,不会睡眠,会持续的尝试,单cpu的时候自旋锁会让其它process动不了. 因此,一般自旋锁实现会有一个参数限定最多持续尝试次数. 超出后, 自旋锁放弃当前time slice. 等下一次机会

由此可见,自旋锁比较适用于锁使用者保持锁时间比较短的情况。正是由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁信号量和读写信号量适合于保持时间较长的情况,它们会导致调用者睡眠,因此只能在进程上下文使用,而自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用。如果被保护的共享资源只在进程上下文访问,使用信号量保护该共享资源非常合适,如果对共享资源的访问时间非常短,自旋锁也可以。但是如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。自旋锁保持期间是抢占失效的,而信号量和读写信号量保持期间是可以被抢占的。自旋锁只有在内核可抢占或SMP(多处理器)的情况下才真正需要,在单CPU且不可抢占的内核下,自旋锁的所有操作都是空操作,

 

总结:

互斥锁与自旋锁的区别

(1)、互斥锁mutex:独占锁;开销大

pthread_mutex_lock(pthread_mutex_t *mutex);

pthread_mutex_unlock(pthread_mutex_t *mutex);

(2)、自旋锁spin lock:轻量级的锁,开销小;适用于短时间内对锁的使用

如果自旋锁已经被其他的执行单元保持,调用者就一直循环在那里判断该自旋锁是否被释放

pthread_spin_lock(pthread_spinlock_t *lock);

pthread_spin_unlock(pthread_spinlock_t *lock);

 

注意:对于spin lock,如果递归调用过深,会导致死锁。

### 自旋锁互斥锁的概念 #### 互斥锁(Mutex) 互斥锁是一种用于保护共享资源不被多个线程同时访问的机制。当一个线程尝试获取已被占用的互斥锁时,该线程会被挂起并进入等待状态,直到持有锁的线程释放锁为止[^2]。 #### 自旋锁(Spin Lock) 自旋锁也是一种同步原语,但是它的行为不同于互斥锁。如果某个线程请求一个已经被占用的自旋锁,则此线程不会立即停止执行而是继续循环检查锁的状态——即“旋转”,直至能够成功取得锁位置[^4]。 ### 自旋锁互斥锁的主要区别 - **阻塞性质** - 对于互斥锁而言,在竞争失败的情况下,进程或线程将会被置于休眠态;对于自旋锁来说,即使未能立刻获得所需资源也会保持活跃状态持续轮询。 - **性能影响** - 使用互斥锁可减少不必要的CPU资源浪费,因为未获准许的操作系统实体处于睡眠模式下并不消耗计算能力。然而这可能导致额外开销如上下文切换等。 - 而采用自旋方式虽然看似简单粗暴却能在某些特定条件下提供更高效的响应速度特别是锁定周期极短的情形之下[^1]。 - **适用环境差异** - 在单核处理器环境中通常推荐优先考虑使用互斥锁来管理并发控制问题。 - 多核心架构下的应用程序里,考虑到跨CPU间的通信延迟等因素,适当运用自旋锁有助于提升整体效率尤其是那些预计占有时间很短暂的关键区域[^3]。 ```cpp // C++代码示例展示如何定义简单的自旋锁类 class SpinLock { private: std::atomic_flag flag = ATOMIC_FLAG_INIT; public: void lock() { while (flag.test_and_set(std::memory_order_acquire)) {} } void unlock() noexcept { flag.clear(std::memory_order_release); } }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值