OSSpinLock(自旋锁)

OSSpinLock叫做”自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源

  • 循环等待访问,不释放当前资源
  • 用于轻量级数据访问,简单的int值 +1/-1操作
  • 目前已经不再安全,可能会出现优先级反转问题
    • 如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁
  • 需要导入头文件#import <libkern/OSAtomic.h>
  • image.png

    OS_unfair_lock

  • os_unfair_lock用于取代不安全的OSSpinLock ,从iOS10开始才支持

  • 从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
  • 需要导入头文件#import <os/lock.h>
  • image.png

    pthread_mutex

  • mutex叫做”互斥锁”,等待锁的线程会处于休眠状态

  • 需要导入头文件#import <pthread.h>
  • image.pngimage.png
  • pthread_mutex – 递归锁

image.png

  • pthread_mutex – 条件

image.png

NSLock

  • 不可重入
  • NSLock是对mutex普通锁的封装

image.png

NSRecursiveLock

  • 可重入
  • NSRecursiveLock也是对mutex递归锁的封装,API跟NSLock基本一致

    NSCondition

  • NSCondition是对mutex和cond的封装

  • image.png

    NSConditionLock

  • NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值

  • image.png

    dispatch_queue

  • 直接使用GCD的串行队列,也是可以实现线程同步的

  • image.png

    dispath_semaphore_t

  • semaphore叫做”信号量”

  • 信号量的初始值,可以用来控制线程并发访问的最大数量
  • 信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步
  • image.png

    @synchronized

  • @synchronized是对mutex递归锁的封装

  • 源码查看:objc4中的objc-sync.mm文件
  • @synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作
  • 一般在创建单例对象的时候使用,保证在多线程下创建对象是唯一的

atomic

  • 修饰属性关键字
  • atomic用于保证属性settergetter的原子性操作,相当于在gettersetter内部加了线程同步的锁
  • 可以参考源码objc4的objc-accessors.mm
  • 对被修饰对象进行原子操作(不负责使用)它并不能保证使用属性的过程是线程安全的

同步方案性能比较

性能从高到低排序

  • os_unfair_lock
  • OSSpinLock
  • dispatch_semaphore
  • pthread_mutex
  • dispatch_queue(DISPATCH_QUEUE_SERIAL)
  • NSLock
  • NSCondition
  • pthread_mutex(recursive)
  • NSRecursiveLock
  • NSConditionLock
  • @synchronized

    自旋锁、互斥锁比较

    什么情况使用自旋锁比较划算?

  • 预计线程等待锁的时间很短

  • 加锁的代码(临界区)经常被调用,但竞争情况很少发生
  • CPU资源不紧张
  • 多核处理器

什么情况使用互斥锁比较划算?

  • 预计线程等待锁的时间较长
  • 单核处理器
  • 临界区有IO操作
  • 临界区代码复杂或者循环量大
  • 临界区竞争非常激烈

    iOS中的读写安全方案

    思考如何实现以下场景

  • 同一时间,只能有1个线程进行写的操作

  • 同一时间,允许有多个线程进行读的操作
  • 同一时间,不允许既有写的操作,又有读的操作

上面的场景就是典型的“多读单写”,经常用于文件等数据的读写操作,iOS中的实现方案有

  • pthread_rwlock:读写锁
  • dispatch_barrier_async:异步栅栏调用

    pthread_rwlock

    等待锁的线程会进入休眠
    image.png

    dispatch_barrier_async

  • 这个函数传入的并发队列必须是自己通过dispatch_queue_cretate创建的

  • 如果传入的是一个串行或是一个全局的并发队列,那这个函数便等同于dispatch_async函数的效果

image.png