介绍
优先级反转(Priority Inversion)是一种计算机科学中的现象,其中一个高优先级的任务被迫等待一个低优先级的任务释放资源,从而导致系统整体性能下降,甚至可能引发死锁等问题。这种现象尤其在多线程编程中容易发生,因为多个线程可能会竞争相同的资源。
优先级反转的工作原理
为了理解优先级反转,我们来看一个简单的例子。假设有三个任务:
- 高优先级任务 A
- 中优先级任务 B
- 低优先级任务 C
低优先级任务 C 持有一个资源(例如一个锁),并正在执行。此时,高优先级任务 A 尝试获取锁,于是进入等待状态。接着,中优先级任务 B 开始执行,因为它的优先级高于 C,但低于 A。由于 B 和 A 无关,它不会释放 C 持有的资源,结果是高优先级任务 A 被中优先级任务 B 间接阻塞。这就是优先级反转。
Quality of Service (QoS)
在 Swift 中,线程的优先级通过 Quality of Service (QoS) 来管理。QoS 定义了不同类型任务的优先级,这有助于系统在调度任务时做出更好的决策。常见的 QoS 有:
.userInteractive:最高优先级,适用于用户交互相关的任务,需要立即完成。.userInitiated:高优先级,适用于用户启动的任务,期望快速完成。.default:默认优先级,适用于一般任务。.utility:低优先级,适用于长时间运行但对性能影响不大的任务。.background:最低优先级,适用于用户不可见的任务,例如数据同步。
优先级反转案例
- 案例一
原因:高优先级任务依赖低优先级任务,造成死锁
解决方法: 调整任务的优先级,使得低优先级依赖高优先级。
//客户端调用
func initBackgroundWork() {
let dispatchSemaphore = DispatchSemaphore(value: 0)
// 高优先级任务
let backgroundQueue = DispatchQueue.global(qos: .userInteractive)
backgroundQueue.async {
self.doBackground {
dispatchSemaphore.signal()
}
// 等待被唤醒
_ = dispatchSemaphore.wait(timeout: .distantFuture)
DispatchQueue.main.async { [weak self] in
self?.label.stringValue = "background work complete"
}
}
}
private func doBackground(completionHandler:@escaping ()->Void) {
// 低优先级任务
let backgroundQueue = DispatchQueue.global(qos: .background)
backgroundQueue.async {
Thread.sleep(forTimeInterval: 2.0)
completionHandler()
}
}
- 案例二
原因: 低优先级任务先执行,然后高优先级任务等待,此时中优先级任务开始占满CPU,使得高优先级任务依赖中优先级任务,发生优先级反转。
解决方法: 调整任务的优先级。
var semaphore = DispatchSemaphore(value: 1)
var sharedResource = 0
let lowPriorityQueue = DispatchQueue(label: "low", qos: .background, attributes: .concurrent)
let highPriorityQueue = DispatchQueue(label: "high", qos: .userInteractive, attributes: .concurrent)
let midPriorityQueue = DispatchQueue(label: "mid", qos: .default, attributes: .concurrent)
// 低优先级线程
lowPriorityQueue.async {
semaphore.wait()
print("Low priority task started")
// 模拟长时间操作
sleep(5)
sharedResource += 1
print("Low priority task finished")
semaphore.signal()
}
// 确保低优先级线程先获取锁
sleep(1)
// 高优先级线程
highPriorityQueue.async {
print("High priority task waiting for lock")
semaphore.wait()
print("High priority task started")
sharedResource += 1
print("High priority task finished")
semaphore.signal()
}
// 中优先级线程
for _ in 0..<12 {
midPriorityQueue.async {
print("Mid priority task running")
performLongTask()
}
}
// 耗时任务
func performLongTask() {
var count = 0
for i in 0..<100_000_000 {
count += i
}
print("Long task finished")
}
总结
优先级反转是多线程编程中需要注意的问题,可能导致性能下降和系统瓶颈。通过理解 QoS 和合理安排任务,可以有效地减轻优先级反转的影响。在实际编程中,合理使用锁和其他同步机制,结合优先级继承技术,可以帮助我们构建更加高效和可靠的系统。