0%

概念

锁:在执行多线程时,用于强行限制资源访问的同步机制,即用于并发控制中保证对互斥要求的满足。

锁都是为了互斥(Mutual exclusion,缩写 Mutex)。即防止两条线程同时对同一公共资源(如全局变量)进行读写的机制。

分类

锁从实现上分两种:

  • 自旋锁。效率高、安全性不足、占用CPU资源大。
  • 非自旋锁。安全性突出、占用CPU资源小,但休眠、唤醒过程要消耗CPU资源。

自旋转锁

忙等待的,会一直在那空转(循环),直到使用锁的一方释放。自旋锁不会让线程状态发生切换,一直处于用户态,即线程一直都是active的,不会让线程进入阻塞/休眠,减少了不必要的上下文切换,执行速度快。

以下情况选用自旋锁:

  • 预计线程等待锁的时间很短;
  • 加锁代码(临界区)经常被调用,但竞争情况发生概率很小,对安全性要求不高;
  • CPU资源不紧张或多核处理器

非自旋锁

非忙等待的,操作内核,将自己的状态改为阻塞挂起来,从待执行队列中移出,等待其他线程唤醒。在获取不到锁的时候会进入阻塞状态,从而进入内核态,当获得锁的时候需要从内核态恢复,需要线程上下文切换,影响锁的性能。

以下情况使用非自旋锁:

  • 预计线程等待锁的时间比较长;
  • 单核处理器;
  • 临界区有IO操作;
  • 临界区代码复杂度、循环量大
  • 临界区竞争非常激烈,对安全性要求高

阻塞与休眠

  • 阻塞:等待一个中断事件的到来
  • 休眠:等待一个超时事件的到来

iOS中的锁

互斥锁:

  • pthread_mutex_t
  • NSLock、NSConditionLock(封装了pthread_mutex_t,attr = 普通)
  • NSDistributedLock(封装了pthread_mutex_t,attr = 递归)
    • 引用计数表的数据结构中使用到,对一张表的多个部分进行同时操作。
  • NSCondition(封装了pthread_mutex_t和pthread_cond_t)
  • @synchronized

递归锁(基于互斥锁):

  • NSRecursiveLock

自旋锁:

  • OSSpinLock

NSLock

普通的互斥锁。通过阻塞线程实现。

NSConditionLock

条件锁。比NSLock多了个NSInteger condition作为相等的条件。与condition相等则加锁。

NSRecursiveLock

递归锁。与NSLock类似,但可以在同一线程重复加锁而不死锁。实现递归过程原子性。

OSSpinLock

自旋锁。

os_unfair_lock

用于替代OSSpinLock,解决了优先级反转的问题,但其本质是互斥锁。atomic内部也使用该锁。

NSCondition

协调线程间的顺序执行。wait-signal。先执行一部分任务,然后跳转到其他地方执行,完了以后再回来。

@synchronize(object)

通过判断传入的对象是否相同,才满足互斥。

dispatch_semaphore

信号量。限制有限数量的资源使用。

pthread_mutex

互斥锁。

实现多线程多读单写

实现方案:

  • pthread_rwlock,读写锁
  • dispatch_barrier_async,异步栅栏调用
    • 需要在一个自己创建的并发队列中执行屏障。
1
2
3
4
5
6
7
8
9
10
11
12
//手动创建一个并发队列
dispatch_queue_t queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{ // 普通异步
/*
读操作代码
*/
});
dispatch_barrier_async(queue, ^{ // 屏障异步
/*
写操作代码
*/
});

参考

欢迎关注我的其它发布渠道