使用 os_unfair_lock 替代 DispatchQueue?!

我们使用 os_unfair_lock 来替代 DispatchQueue 作为锁机制。注意,os_unfair_lock 是 iOS 10.0+ 和 macOS 10.12+ 可用的。

使用 os_unfair_lock 时需要注意,它必须被存储在适当的位置(例如,作为实例变量或静态变量),并且必须确保锁被正确初始化和使用。

另外,os_unfair_lock 是一个互斥锁,并且不像 DispatchQueue 那样可以用于异步任务。它只是用于同步代码的锁。

我们将创建一个线程安全的类,使用 os_unfair_lock 来保护对其内部数据的访问。

注意:os_unfair_lock 必须使用 OS_UNFAIR_LOCK_INITIALIZER 初始化,或者使用 init 方法(在 Swift 中,init() 会返回一个初始化的锁)。

由于 os_unfair_lock 是值类型(在 Swift 中是一个结构体),我们可以将其作为属性存储。

但是请注意,os_unfair_lock 必须使用 & 传递给系统函数,因此我们在锁定时需要将其声明为变量(即可变的)。

我们来看一个示例,如何使用 os_unfair_lock 来保护一个简单的计数器。

另外,需要注意,os_unfair_lock 不能递归使用,即同一个线程不能多次锁定,否则会导致死锁。

我们使用以下步骤:

  1. 声明一个 os_unfair_lock 属性,并初始化为 OS_UNFAIR_LOCK_INITIALIZER。

  2. 在需要加锁的代码块中,使用 os_unfair_lock_lock 和 os_unfair_lock_unlock。

但是,在 Swift 中,我们也可以使用 withLock 类似的模式来确保锁的获取和释放。

由于 os_unfair_lock 是低级锁,我们需要注意避免在锁定期间抛出错误,确保在退出时释放锁。

我们可以设计一个类似于 DispatchQueue.sync 的闭包执行方式,但是使用 os_unfair_lock。

以下详细解释如何用 os_unfair_lock 替代 DispatchQueue 实现线程同步

1. 基本用法对比

使用 DispatchQueue(之前的方式)

复制代码
class DataManager {
    private let queue = DispatchQueue(label: "com.example.datamanager", attributes: .concurrent)
    private var data: [String: Any] = [:]
    
    func setValue(_ value: Any, forKey key: String) {
        queue.async(flags: .barrier) {
            self.data[key] = value
        }
    }
    
    func getValue(forKey key: String) -> Any? {
        var result: Any?
        queue.sync {
            result = self.data[key]
        }
        return result
    }
}

使用 os_unfair_lock(优化后)

复制代码
import os.lock

class DataManager {
    private var lock = os_unfair_lock()
    private var data: [String: Any] = [:]
    
    func setValue(_ value: Any, forKey key: String) {
        os_unfair_lock_lock(&lock)
        defer { os_unfair_lock_unlock(&lock) }
        data[key] = value
    }
    
    func getValue(forKey key: String) -> Any? {
        os_unfair_lock_lock(&lock)
        defer { os_unfair_lock_unlock(&lock) }
        return data[key]
    }
}

2. 封装成易用的类型

复制代码
import os.lock

final class UnfairLock {
    private var _lock = os_unfair_lock()
    
    func lock() {
        os_unfair_lock_lock(&_lock)
    }
    
    func unlock() {
        os_unfair_lock_unlock(&_lock)
    }
    
    func tryLock() -> Bool {
        return os_unfair_lock_trylock(&_lock)
    }
    
    func withLock<T>(_ block: () throws -> T) rethrows -> T {
        lock()
        defer { unlock() }
        return try block()
    }
}

// 使用示例
class ThreadSafeContainer<T> {
    private let lock = UnfairLock()
    private var storage: [T] = []
    
    func append(_ element: T) {
        lock.withLock {
            storage.append(element)
        }
    }
    
    var count: Int {
        lock.withLock {
            storage.count
        }
    }
    
    func removeAll() {
        lock.withLock {
            storage.removeAll()
        }
    }
}

3. 处理复杂场景

读写锁(Read-Write Lock)

复制代码
final class ReadWriteLock {
    private var lock = os_unfair_lock()
    private var readers = 0
    
    func read<T>(_ block: () throws -> T) rethrows -> T {
        os_unfair_lock_lock(&lock)
        readers += 1
        if readers == 1 {
            // 第一个读者,这里可以设置写锁
        }
        os_unfair_lock_unlock(&lock)
        
        defer {
            os_unfair_lock_lock(&lock)
            readers -= 1
            if readers == 0 {
                // 最后一个读者,这里可以释放写锁
            }
            os_unfair_lock_unlock(&lock)
        }
        
        return try block()
    }
    
    func write<T>(_ block: () throws -> T) rethrows -> T {
        os_unfair_lock_lock(&lock)
        defer { os_unfair_lock_unlock(&lock) }
        return try block()
    }
}

4. 性能对比和注意事项

性能优势:

  • os_unfair_lockDispatchQueue 性能更高,尤其是在高频锁操作场景

  • 内存占用更小(仅 4 字节)

  • 没有线程优先级反转问题

重要注意事项:

  1. 不可递归锁

    // ❌ 错误:会导致死锁
    lock.withLock {
    lock.withLock { // 同一个线程再次加锁会死锁
    // ...
    }
    }

2.不可在锁内休眠

复制代码
// ❌ 错误:不要在锁内执行耗时操作
lock.withLock {
    Thread.sleep(forTimeInterval: 1)  // 可能导致性能问题
    // ...
}

3、Swift 5.7+ 的新 API

复制代码
// iOS 16+ / macOS 13+ 可以使用更安全的 API
import os.lock

@available(iOS 16.0, macOS 13.0, *)
class ModernLock {
    private let lock = OSAllocatedUnfairLock()
    
    func updateData() {
        lock.withLock {
            // 线程安全操作
        }
    }
}

5. 完整示例

复制代码
import os.lock

class ThreadSafeCache<Key: Hashable, Value> {
    private struct WrappedLock {
        var lock = os_unfair_lock()
    }
    
    private var locks: [WrappedLock]
    private var storage: [Key: Value]
    
    init(capacity: Int = 16) {
        storage = [:]
        locks = (0..<capacity).map { _ in WrappedLock() }
    }
    
    private func lockIndex(for key: Key) -> Int {
        return abs(key.hashValue) % locks.count
    }
    
    func set(_ value: Value, for key: Key) {
        let index = lockIndex(for: key)
        os_unfair_lock_lock(&locks[index].lock)
        defer { os_unfair_lock_unlock(&locks[index].lock) }
        storage[key] = value
    }
    
    func get(_ key: Key) -> Value? {
        let index = lockIndex(for: key)
        os_unfair_lock_lock(&locks[index].lock)
        defer { os_unfair_lock_unlock(&locks[index].lock) }
        return storage[key]
    }
    
    func remove(_ key: Key) {
        let index = lockIndex(for: key)
        os_unfair_lock_lock(&locks[index].lock)
        defer { os_unfair_lock_unlock(&locks[index].lock) }
        storage.removeValue(forKey: key)
    }
}

选择建议

使用 os_unfair_lock 当:

  • 需要高性能的同步

  • 锁持有时间很短

  • 不需要递归锁特性

  • 运行在 iOS 10+ / macOS 10.12+

继续使用 DispatchQueue 当:

  • 需要更高级的功能(如 barrier、async/sync 分离)

  • 需要递归锁

  • 需要在锁内执行异步操作

  • 代码需要在更老的系统版本运行

os_unfair_lock 在性能关键路径上是更好的选择,但需要更小心地使用以避免死锁。

相关推荐
ZZH_AI项目交付5 小时前
同一套前摄心率算法,为什么 iPhone 16 Pro 稳,iPhone XR 会失准?
ios·app·ai编程
DeMinds6 小时前
内容没有丢,我为什么总在重新整理?|DeMinds 如何让工作接着继续
ios·github·markdown
apd_csdn6 小时前
清华邮箱苹果邮件app设置(全)
ios·thu
MDM.Plus7 小时前
从“遥控”到“自治”:苹果 MDM 技术的代际跨越与业务重构
ios·智能手机·重构·mdm
黑化旺仔8 小时前
iOS - 天气预报仿写总结
ios
Coffeeee9 小时前
ios零基础的Android开发能否靠AI让老板省一笔人工费呢
android·ios·ai编程
2501_916007479 小时前
深入理解HTTPS对称与非对称加密机制及Charles抓包实践
网络协议·http·ios·小程序·https·uni-app·iphone
2501_916008899 小时前
HTTPS 抓包遇到证书绑定怎么办,使用 TraceEagle 解除 App 证书校验
网络协议·计算机网络·http·网络安全·ios·adb·https
白玉cfc9 小时前
【iOS】MRC和ARC
macos·ios·cocoa
2501_9159214311 小时前
从零开始学 Swift iOS 开发 iOS应用入门
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程