为什么拥有spinlock的时候不可以sleep

本文解析了在持有自旋锁时为什么不能让进程进入睡眠状态的原因。通过分析自旋锁的工作原理,指出若持有自旋锁的进程进入睡眠状态会导致进程调度无法将CPU重新分配给该进程,进而引发死锁。详细解释了自旋锁机制及其与进程睡眠之间的冲突。

参考:http://stackoverflow.com/questions/4752031/why-cant-you-sleep-while-holding-spinlock

首先看下,spin_lock的实现:

static inline void spin_lock(spinlock_t *lock)
{
	raw_spin_lock(&lock->rlock);
}

 

#define raw_spin_lock(lock)	_raw_spin_lock(lock)

void __lockfunc _raw_spin_lock(raw_spinlock_t *lock)
{
	__raw_spin_lock(lock);
}
static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
	preempt_disable();
	spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
	LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}


最终调用spin_lock的时候,我们发现它会去调用preempt_disable(),不让进程可以抢占
1
很多书上说spin_lock的时候,不可以sleep.
确切的说,应该说是你这个时候用是个非常不明智的选择。
2
sleep 意味着进程主动让出cpu的控制权,上下文切换到别的可以运行的进程,但是却没有释放spinlock
3
  假设进程A,首先获得spinlock,这个是时候A sleep 主动让出CPU。
  kernel 调度到进程B , 且B也试图去获取同样的spinlock , B会一直自旋 ,
  kernel 已经不可能再调度到A(不可抢占,B会一直拥有CPU,直到主动放弃),A 也就不可能去释放spinlock
  B 也会永远spin下去 , 呵呵,这个就相当于导致了一个死锁的发生。


1
It's not that you can't sleep while holding a spin lock. It is a very very bad idea to do that
2
assume that a process sleeps while holding a spilock. If the new process that is scheduled tries to acquire the same spinlock, it starts spinning for the lock to be available. Since the new process keeps spinning, it is not possible to schedule the first process and thus the lock is never released making the second process to spin for ever and we have a deadlock
3
Sleep() means a thread/process yields the control of the CPU and CONTEXT SWITCH to another, without releasing the spinlock, that's why it's wrong.

### 什么是 SpinLockSpinLock(自旋)是一种低级的同步机制,用于在多线程环境中保护共享资源。与互斥(Mutex)同,SpinLock 在线程无法立即获取会让线程进入休眠状态,而是通过一个 **busy-wait-loop**(忙等待循环)断尝试获取,直到成功为止。这种方式避免了线程状态切换(用户态 ↔ 内核态)所带来的开销[^2]。 ### SpinLock 在多线程编程中的作用 1. **资源保护**:SpinLock 可以确保在同一时刻只有一个线程可以访问共享资源,从而防止数据竞争和一致的问题。 2. **减少上下文切换开销**:由于 SpinLock 会使线程休眠,因此避免了线程状态切换的代价,适用于持有时间非常短的场景。 3. **提高并发性能**:在多核处理器上,SpinLock 能够让等待的线程保持运行状态,从而在释放后迅速获得控制权,提升整体吞吐量[^4]。 ### SpinLock 的使用场景 1. **短临界区**:SpinLock 最适合用于临界区(即需要保护的代码段)非常短的情况。如果临界区执行时间较长,忙等待会导致 CPU 使用率飙升,降低系统性能[^2]。 2. **多核系统**:在多核或多处理器系统中,SpinLock 的性能通常优于 Mutex,因为等待的线程可以在另一个核心上持续运行并尝试获取[^3]。 3. **性能敏感场景**:当通过性能分析发现传统的同步机制(如 `Mutex` 或 `Monitor`)成为瓶颈时,可以考虑使用 SpinLock 来优化性能[^3]。 ### SpinLock 的局限性 1. **CPU 资源浪费**:在等待获取的过程中,线程会持续占用 CPU 时间,可能导致 CPU 使用率升高。 2. **线程优先级问题**:如果持有的线程因调度原因被延迟执行,等待的线程可能会长时间自旋,导致资源浪费甚至“线程饥饿”问题[^5]。 3. **适合长时间持有**:SpinLock 适合用于保护执行时间较长的临界区,因为这会显著增加其他线程的等待成本。 ### 示例代码(C++) 以下是一个简单的 SpinLock 实现示例,使用 C++ 的 `std::atomic_flag`: ```cpp #include <atomic> #include <thread> #include <iostream> class SpinLock { private: std::atomic_flag flag = ATOMIC_FLAG_INIT; public: void lock() { while (flag.test_and_set(std::memory_order_acquire)) { // 自旋等待 } } void unlock() { flag.clear(std::memory_order_release); } }; SpinLock spinlock; int shared_data = 0; void thread_func() { for (int i = 0; i < 100000; ++i) { spinlock.lock(); shared_data++; spinlock.unlock(); } } int main() { std::thread t1(thread_func); std::thread t2(thread_func); t1.join(); t2.join(); std::cout << "Shared data value: " << shared_data << std::endl; return 0; } ``` ### 总结 SpinLock 是一种轻量级的同步机制,适用于多核系统中短临界区的保护场景。它通过避免线程状态切换来提升性能,但使用当可能导致 CPU 资源浪费。因此,在选择 SpinLock 时,必须确保临界区足够短,并且经过性能分析确认其适用性[^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值