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 封装了加锁和释放锁的逻辑,简化了多线程编程。它适用于需要递归加锁的场景(如嵌套函数调用),并通过内联优化提升了性能。

相关推荐
90后的晨仔6 小时前
RxSwift 中的 `Single`:单元素响应式编程简单实践
ios
二流小码农6 小时前
鸿蒙开发:CodeGenie万能卡片生成
android·ios·harmonyos
imLix6 小时前
APP-启动优化-1-冷启动流程
ios
众乐 认证8 小时前
ios 26发布:设计革新与智能整合
ios·carplay·ultra
90后的晨仔9 小时前
RxSwift 中的 Observable和它的使用方式
ios
90后的晨仔9 小时前
RxSwift 中 Observable 的核心方法简介
ios
90后的晨仔9 小时前
RxSwift实战:从传统开发到响应式编程的代码示例
ios
90后的晨仔14 小时前
RxSwift 源码解析:深入 ObservableType 扩展与订阅机制
ios
90后的晨仔14 小时前
Swift 中的`@dynamicMemberLookup`是什么?
ios