使用 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 在性能关键路径上是更好的选择,但需要更小心地使用以避免死锁。

相关推荐
2501_915106324 小时前
iOS 抓包工具有哪些?不同类型的抓包工具可以做什么
android·ios·小程序·https·uni-app·iphone·webview
一点晖光7 小时前
ios底部按钮被挡住
前端·ios·微信小程序
chinesegf17 小时前
iTunes Lookup API 规则具体(查包名)
ios
C+++Python1 天前
iOS 长截图
ios
货拉拉技术1 天前
iOS疑难Crash-_dispatch_barrier_waiter_redirect_or_wake 崩溃治理
ios
chinesegf1 天前
iOS 内购收据验证的基础实现
ios
TheNextByte11 天前
如何在没有 Wi-Fi 的情况下备份 iPhone
ios·iphone
2501_915106321 天前
iOS开发中CPU功耗监控的实现与工具使用
android·macos·ios·小程序·uni-app·cocoa·iphone
chinesegf1 天前
如何在沙盒环境中进行内购测试
笔记·ios