线程锁

2020-07-09  本文已影响0人  六横六竖亚

多线程下为了访问安全,对同一块资源按照顺序进行的线程同步技术,加锁最常见。

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信号量,由于是切时间片所以不会产生优先级反转)

上一篇下一篇

猜你喜欢

热点阅读