swift
protocol Lock {
func lock()
func unlock()
}
// https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000321.html
typealias SpinLock = RecursiveLock
extension RecursiveLock : Lock {
@inline(__always)
final func performLocked<T>(_ action: () -> T) -> T {
self.lock(); defer { self.unlock() }
return action()
}
}
这段代码是RxSwift
的DisposeBag
类中的一个有关锁子的代码,以下是一个简单的分析:
1. 定义 Lock
协议
swift
protocol Lock {
func lock()
func unlock()
}
- 功能 :定义一个锁的通用接口,要求实现两个方法:
lock()
:获取锁。unlock()
:释放锁。
2. 类型别名:SpinLock = RecursiveLock
swift
typealias SpinLock = RecursiveLock
- 作用 :将
SpinLock
定义为RecursiveLock
的别名。 - 含义 :
SpinLock
是一个递归锁(RecursiveLock
),允许同一线程多次加锁而不会死锁。
3. 扩展 RecursiveLock
符合 Lock
协议
swift
extension RecursiveLock : Lock {
@inline(__always)
final func performLocked<T>(_ action: () -> T) -> T {
self.lock(); defer { self.unlock() }
return action()
}
}
- 目的 :让
RecursiveLock
实现Lock
协议中的lock()
和unlock()
方法。 - 关键点 :
@inline(__always)
:强制编译器内联此方法,减少函数调用开销。final
:禁止子类重写此方法。performLocked
:- 获取锁(
self.lock()
)。 - 使用
defer
确保在作用域结束时自动释放锁(self.unlock()
)。 - 在锁定期间执行传入的闭包
action
,返回结果。
- 获取锁(
- 疑问 :为啥没有使用自旋锁呢? 代码中给了一个邮件地址
大致的意思说了:iOS 中自旋锁(spin lock)会有优先级反转的问题。(如高优先级线程被低优先级线程阻塞)。
因此,RxSwift 使用 递归锁(RecursiveLock) 替代自旋锁,确保线程安全。
4. 代码设计意图
为什么使用递归锁(RecursiveLock
)?
- 递归锁的特点 :
- 同一线程可以多次加锁(递归调用
lock()
),但必须等次数相等的unlock()
后才会真正释放锁。 - 避免死锁:例如,在加锁后调用另一个需要加锁的方法时,不会因为重复加锁导致死锁。
- 同一线程可以多次加锁(递归调用
- 适用场景 :
- 递归调用需要加锁的函数(如嵌套函数)。
- 多层逻辑需要共享同一把锁。
performLocked
的作用
- 线程安全封装 :
- 将加锁、执行操作、释放锁的逻辑封装成一个方法,确保无论操作是否抛出异常或提前返回,锁都会被正确释放。
- 简化使用 :
- 开发者无需手动调用
lock()
和unlock()
,只需通过performLocked
提供闭包即可。
- 开发者无需手动调用
5. 示例:如何使用这段代码
假设我们有一个需要线程安全的计数器:
swift
class ThreadSafeCounter {
private var count = 0
private let lock = RecursiveLock()
func increment() {
lock.performLocked {
count += 1
}
}
func get() -> Int {
lock.performLocked {
return count
}
}
}
多线程测试
swift
let counter = ThreadSafeCounter()
DispatchQueue.concurrentPerform(iterations: 1000) { _ in
counter.increment()
}
print(counter.get()) // 输出 1000
- 说明 :
- 多个线程同时调用
increment()
,performLocked
确保每次操作是线程安全的。 - 最终计数器值为 1000,验证了线程安全性。
- 多个线程同时调用
6. 与 Swift 内存管理的关联
根据你的知识库中提到的 Swift 开发邮件列表讨论(如 SpinLock
与 weak
属性的线程安全问题),这段代码的设计可能涉及以下背景:
- 递归锁与线程安全 :
RecursiveLock
是一种轻量级锁,适合需要多次加锁的场景(如嵌套函数调用)。- Swift 的
weak
属性在多线程下可能引发竞争条件,使用锁可以确保对共享资源的访问是原子的。
- 性能优化 :
@inline(__always)
强制内联performLocked
,减少函数调用开销,适合高频调用的场景。
7. 注意事项
- 避免死锁 :
- 不要跨线程传递锁对象,否则可能导致死锁。
- 递归锁虽然允许同一线程多次加锁,但需注意解锁次数是否匹配。
- 递归锁 vs 普通锁 :
- 如果不需要递归调用,使用普通锁(如
NSLock
)性能更高。 - 递归锁会增加内存开销,仅在必要时使用。
- 如果不需要递归调用,使用普通锁(如
- 适用场景 :
- 线程安全的数据结构(如计数器、队列)。
- 多线程环境下的回调函数或事件处理。
总结
这段代码通过 RecursiveLock
实现了一个线程安全的锁机制,结合 performLocked
封装了加锁和释放锁的逻辑,简化了多线程编程。它适用于需要递归加锁的场景(如嵌套函数调用),并通过内联优化提升了性能。