To The West - Locks in iOS

97 阅读2分钟

锁的分类

分为自旋锁和互斥锁,递归锁,条件锁,信号量是基于这两类锁的封装。

  • 互斥锁 mutex 防止多个线程对同一资源进行读写,当一个线程获取锁失败,线程进入等待,直到锁被释放时被唤醒。
    • 递归锁: 可重入,同一个线程可在锁释放前多次获取锁,递归调用。
    • 非递归锁:不可重入,需要等锁被释放后再次获取
    • pthread_metex YYMemoryCache 使用
    • @synchronized
    • NSLock
  • 自旋锁:忙等待,不进入休眠
    • OSSpinLock,iOS 10已废弃
    • atomic, os_unfiar_lock 代替,对读写加锁
    • pthread_rwlock_t 对共享资源读写保护,一般用栅栏函数代替

Synchronized

  • 实现 objc_sync_enter & objc_sync_exit方法
  • recursive_metex_t 属于递归锁
  • 递归过程obj不存在则终止递归防止死锁
  • 底层全局的哈希表 image.png
  • 快速缓存 定义 sync_data_direct_key 和 sync_count_direct_key 与 TLS 的 key 对应,从缓存中找到对象 SyncCacheItem.data 和 SyncCacheItem.lockCount
    • 获取资源,lockCount++, 更新写入
    • 释放资源 lockCount--,更新写入。如果为 0,从缓存中移除,并清空线程数 threadCount
    • 查看资源不操作

Synchronized QA

  • 不能使用非 OC 对象,接受 id2data 接受 id 类型
  • 多次锁同一个对象 - 只锁一次,从快速缓存中获取对象
  • 性能低 - 增删改查成本
  • 加锁对象 nil 失效,不保证线程安全

NSLock

对 pthread_metex 的简单封装,AFURLSessionManager 有使用,不能递归调用

NSRecursiveLock

  • 递归锁,在 YYWebImageOperation使用
  • for 循环+异步调用递归锁可能导致死锁

性能对比

image.png

总结

  • OSSpinLock不再安全,底层用os_unfair_lock替代
  • atomic只能保证setter、getter时线程安全,所以更多的使用nonatomic来修饰
  • 读写锁更多使用栅栏函数来实现
  • @synchronized在底层维护了一个哈希链表进行data的存储,使用recursive_mutex_t进行加锁
  • NSLockNSRecursiveLockNSConditionNSConditionLock底层都是对pthread_mutex的封装
  • NSConditionNSConditionLock是条件锁,当满足某一个条件时才能进行操作,和信号量dispatch_semaphore类似
  • 普通场景下涉及到线程安全,可以用NSLock
  • 循环调用时用NSRecursiveLock
  • 循环调用且有线程影响时,请注意死锁,如果有死锁问题请使用@synchronized

Reference

juejin.cn/post/684490…