RxSwift 的DisposeBag中的SpinLock()分析

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()
    }
}

这段代码是RxSwiftDisposeBag类中的一个有关锁子的代码,以下是一个简单的分析:

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 开发邮件列表讨论(如 SpinLockweak 属性的线程安全问题),这段代码的设计可能涉及以下背景:

  1. 递归锁与线程安全
    • RecursiveLock 是一种轻量级锁,适合需要多次加锁的场景(如嵌套函数调用)。
    • Swift 的 weak 属性在多线程下可能引发竞争条件,使用锁可以确保对共享资源的访问是原子的。
  2. 性能优化
    • @inline(__always) 强制内联 performLocked,减少函数调用开销,适合高频调用的场景。

7. 注意事项

  1. 避免死锁
    • 不要跨线程传递锁对象,否则可能导致死锁。
    • 递归锁虽然允许同一线程多次加锁,但需注意解锁次数是否匹配。
  2. 递归锁 vs 普通锁
    • 如果不需要递归调用,使用普通锁(如 NSLock)性能更高。
    • 递归锁会增加内存开销,仅在必要时使用。
  3. 适用场景
    • 线程安全的数据结构(如计数器、队列)。
    • 多线程环境下的回调函数或事件处理。

总结

这段代码通过 RecursiveLock 实现了一个线程安全的锁机制,结合 performLocked 封装了加锁和释放锁的逻辑,简化了多线程编程。它适用于需要递归加锁的场景(如嵌套函数调用),并通过内联优化提升了性能。

相关推荐
2501_9151063232 分钟前
iOS 可分发是已经上架了吗?深入解析应用分发状态、ipa 文件上传、TestFlight 测试与 App Store 审核流程
android·ios·小程序·https·uni-app·iphone·webview
2501_916007479 小时前
HTTPS 抓包乱码怎么办?原因剖析、排查步骤与实战工具对策(HTTPS 抓包乱码、gzipbrotli、TLS 解密、iOS 抓包)
android·ios·小程序·https·uni-app·iphone·webview
2501_9160088915 小时前
HTTPS 双向认证抓包实战,原理、难点、工具与可操作的排查流程
网络协议·http·ios·小程序·https·uni-app·iphone
2501_9151063215 小时前
HTTPS 能抓包吗?实战答案与逐步可行方案(HTTPS 抓包原理、证书Pinning双向认证应对、工具对比)
网络协议·http·ios·小程序·https·uni-app·iphone
游戏开发爱好者815 小时前
App HTTPS 抓包实战,原理、常见问题与可行工具路线(开发 测试 安全 角度)
网络协议·安全·ios·小程序·https·uni-app·iphone
2501_9151063215 小时前
App HTTPS 抓包实战指南,原理、常见阻碍、逐步排查与工具组合
网络协议·http·ios·小程序·https·uni-app·iphone
CocoaKier16 小时前
苹果海外老账号续费,踩了个大坑!
ios·apple
2501_915106321 天前
上架 App 全流程解析,iOS 应用上架步骤、App Store 审核流程、ipa 文件上传与测试分发经验
android·ios·小程序·https·uni-app·iphone·webview
2501_916013741 天前
苹果应用上架全流程指南 iOS 应用发布步骤、App Store 审核流程、ipa 文件上传与 uni-app 打包实战经验
android·ios·小程序·uni-app·cocoa·iphone·webview