1. 什么是忙等待?
忙等待(Busy Waiting),也被称为“自旋(Spinning)”或“忙循环(Busy Loop)”,是一种进程同步机制。在这种机制下,一个进程会持续检查某个条件是否满足,以决定是否继续执行。例如,它可能在等待某个锁(lock)或资源是否可用。
举个例子:假设一个进程需要访问某个资源,但该资源当前正被其他进程占用。此时,这个进程不会进入休眠状态,而是不断循环检查资源是否释放。这种持续检查的行为就是“忙等待”。
如下图所示,进程在等待期间持续占用CPU资源:
操作系统中常见的两种等待方式如下:
✅ 忙等待:进程持续检查条件,期间持续占用CPU资源。
❌ 阻塞等待(Sleep Waiting):进程进入休眠状态,等待条件满足后被唤醒,期间不占用CPU资源。
2. 忙等待的使用场景
忙等待通常用于实现互斥(Mutual Exclusion)。互斥机制确保多个进程不会同时访问共享资源,从而避免竞争条件(Race Condition)。
在互斥机制中,进程在进入临界区(Critical Section)时必须获得资源的独占访问权限。此时如果资源已被占用,进程可以选择忙等待,直到资源释放。
3. 忙等待的缺点
虽然实现简单,但忙等待存在以下明显问题:
- 资源浪费:进程持续检查条件会浪费CPU资源。
- 系统效率下降:低优先级任务占用CPU时间,而高优先级任务无法及时执行。
- 不适合长时间等待:若等待时间较长,忙等待会严重影响系统性能。
3.1 解决方案一:使用延迟函数(Sleep)
大多数操作系统采用“延迟函数”来缓解忙等待带来的资源浪费问题。延迟函数会将进程置为休眠状态一段时间,期间不占用CPU资源。例如:
algorithm delay(process p, resource z):
// INPUT
// p = 需要延迟的进程
// z = 进程正在等待的资源
// OUTPUT
// 将进程 p 置于休眠状态,直到资源 z 可用
while z is still in use:
p.sleep(900)
进程休眠后会在指定时间被唤醒。若资源仍未可用,可逐步增加休眠时间,直到条件满足。
3.2 解决方案二:使用信号量(Semaphore)阻塞进程
另一种更高效的方式是使用信号量(Semaphore)机制。进程在等待时被放入等待队列,不占用CPU资源。当资源可用时,系统将其唤醒并放入就绪队列。
这种方式避免了忙等待的资源浪费问题,是大多数现代操作系统推荐的做法。
4. 忙等待的优点
尽管存在资源浪费问题,但在某些特定场景下,忙等待仍具有一些优势:
✅ 实现简单:逻辑清晰,易于理解和实现。
✅ 适用于短时间等待:如果等待时间非常短,忙等待的开销可以忽略不计。
✅ 适合用于自旋锁(Spinlock):在并发访问资源较少的系统中,Spinlock 可以提供高效的同步机制。
例如,自旋锁(Spinlock) 是一种常见的忙等待实现。它在多线程环境中用于确保对共享资源的互斥访问。当资源不可用时,线程进入自旋状态,不断检查资源是否释放。一旦资源释放,线程立即恢复执行。
5. 总结
忙等待是一种简单但低效的同步机制。它适用于等待时间短、资源竞争不激烈的场景,但在大多数现代系统中,更推荐使用 sleep 或信号量机制来替代。
✅ 优点:实现简单,适合短时间等待,可用于自旋锁。
❌ 缺点:资源浪费严重,不适合长时间等待,影响系统性能。
在开发中,我们应根据具体场景选择合适的同步机制。合理使用忙等待可以提升性能,滥用则可能成为系统瓶颈。