线程锁
多线程下为了访问安全,对同一块资源按照顺序进行的线程同步技术,加锁最常见。
OSSpinLock 自旋锁
申请锁的操作为原子操作,保证只有一个线程在申请锁。
原理:执行do-while循环,忙等,不切线程,适合小的等待操作,效率最高。
缺点:1、递归调用可能死锁;2、其他线程未获得锁的情况下一直自旋占用CPU;3、多线程下的优先级反转问题(低优先级线程先获得锁,高优先级线程循环忙等申请锁占用大量CPU,低优先级线程无法争抢到资源导致无法完成任务释放锁)
注:iOS10下推出os_unfair_lock_t来代替,线程会进入休眠而非忙等。
pthread_mutex 互斥锁
原理和信号量类似,遇锁时让出时间片进入休眠,等待上下文切换唤醒,由于系统切换上下文需要时间,所以比较适合等待时间较长的场景(如果等待时间较短,忙等比休眠更高效且不消耗CPU资源)。总体性能仅次于自旋锁和信号量。
NORMAL普通锁:等待队列,先进先出获得锁。
ERRORCHECK检错锁(NSLock):同一个线程请求锁会返回错误防止嵌套死锁,其他线程访问同普通锁。
RECURSIVE递归锁(NSRecursiveLock):允许同一线程多次获得锁和多次unlock解锁,要一一对应。
DEFAULT适应锁:锁释放后重新竞争,没有等待队列。
互斥锁和自旋锁的比较
自旋锁会产生优先级反转,用互斥锁会比较安全。
自旋锁在循环等待的时候会消耗cpu的性能。
互斥锁在cpu线程调度的时候会消耗cpu性能。
所以:互斥锁,比较适合临界代码比较耗时间的,如有网络阻塞、IO阻塞的情况。自旋锁, 因为一直消耗cpu,所以一般比较适合临界代码比较少的,比较适合短时间操作的,如从mutable对象里面(dictioanry array hashtable) 读写操作的情况。
dispatch_semaphore 信号量加锁
GCD的一种同步方式,采用信号量的实现原理加锁,性能第二。
signal = dispatch_semaphore_create(long value > 0) //创建信号量
dispatch_semaphore_signal(signal) //传入的信号量+1
dispatch_semaphore_wait(signal, timeout) //信号量>0则执行代码,并信号量-1;若=0则进入wait;wait过程中执行了dispatch_semaphore_signal,则继续执行;如果wait直到timeOut,也会继续执行。
注:互斥锁和信号量的区别?互斥锁主要对资源加锁,信号量更偏向于流程的概念,可以跨线程。
@synchronize 同步锁
@synchronize(self) { //lock,do Something }
传入对象作为标记,Runtime为对象创建一个递归锁存在hash表中。牺牲性能,提供可读性和用法便利。
atomic原子操作
底层实现:get方法(objc_getProperty)、set方法(reallySetProperty)内,用了spinlock_t(其实底层用了mutex_t互斥锁替换以前的自旋锁)锁进行加锁解锁操作。
注:atomic一定线程安全吗?否。因为只锁了get、set方法,只保证进入时安全,不保证多线程访问下的资源安全。
相关问题
自旋锁和互斥锁的区别,分别有什么优缺点
(OSSpinLock是执行循环忙等不切线程,适合小的等待操作,效率高;互斥锁是让出时间片进入休眠等待切换上下文唤醒,适合等待时间长的场景自旋锁多线程优先级反转问题,会导致高优先级线程忙等占用大量CPU,所以用unfair替换了)
互斥锁有哪些类型
(普通锁:先进先出的等待队列;检错锁:同一线程请求锁会返回错误;递归锁:允许同一线程多次对应的加解锁;适应锁:释放后重新竞争,没有等待队列)
互斥锁和信号量的区别
(互斥锁主要对资源加锁,信号量更偏向于流程的概念,可以跨线程。)
atomic一定线程安全吗
(否。因为原子操作只锁了属性的get和set方法(用了os_unfair_lock)。)
优先级反转问题
(自旋锁由于忙等,低优先级任务加锁后,被高优先级任务抢占时间片,导致锁无法释放的问题解决:优先级继承(继承最高的优先级),优先级天花板(进入临界区都设置为最高优先级),禁止中断。用其他锁代替:pthread_mutex互斥锁或semaphore信号量,由于是切时间片所以不会产生优先级反转)