世界那么大我想去看看

iOS面试要点之多线程面试要点

2020-12-17  本文已影响0人  iOS开发面试题技术合集

作者:akon
原文地址:https://xiaozhuanlan.com/topic/7368240915

本篇文章为大家梳理下iOS多线程面试要点。

多线程

多线程创建方式

iOS创建多线程方式主要有NSThread、NSOperation、GCD,这三种方式创建多线程的优缺点如下:

NSThread

NSOperation

GCD

多线程同步

多线程情况下访问共享资源需要进行线程同步,线程同步一般都用锁实现。从操作系统层面,锁的实现有临界区、事件、互斥量、信号量等。这里讲一下iOS中多线程同步的方式。

atomic

使用atomic 修饰属性,编译器会设置默认读写方法为原子读写,底层采用自旋锁(iOS10开始自旋锁改为互斥锁实现了)保证原子操作。
单独的原子操作绝对是线程安全的,但是组合一起的操作就不能保证。一般我们在定义属性的时候用nonatomic,避免性能损失。
参考资料:atomic实现原理

@synchronized

@synchronized指令是一个对象锁,用起来非常简单。使用obj为该锁的唯一标识,只有当标识相同时,才为满足互斥,如果线程1和线程2中的@synchronized后面的obj不相同,则不会互斥。@synchronized其实是对pthread_mutex递归锁的封装。
@synchronized优点是我们不需要在代码中显式的创建锁对象,使用简单; 缺点是@synchronized会隐式的添加一个异常处理程序,该异常处理程序会在异常抛出的时候自动的释放互斥锁,从而带来额外开销。

NSLock

最简单的锁,调用lock获取锁,unlock释放锁。如果其它线程已经调用lock获取了锁,当前线程调用lock方法会阻塞当前线程,直到其它线程调用unlock释放锁为止。NSLock使用简单,在项目中用的最多。

NSRecursiveLock

递归锁主要用来解决同一个线程频繁获取同一个锁而不造成死锁的问题。注意lock和unlock调用必须配对。

NSConditionLock

条件锁,可以设置自定义条件来获取锁。比如生产者消费者模型可以用条件锁来实现。

NSCondition

条件,操作系统中信号量的实现,方法- (void)wait和- (BOOL)waitUntilDate:(NSDate *)limit用来等待锁直至锁有信号;方法- (void)signal和- (void)broadcast使condition有信号,通知等待condition的线程,变成非阻塞状态。

dispatch_semaphore_t

信号量的实现,可以实现控制GCD队列任务的最大并发量,类似于NSOperationQueue的maxConcurrentOperationCount属性。

pthread_mutex

mutex叫做”互斥锁”,等待锁的线程会处于休眠状态。使用pthread_mutex_init创建锁,使用pthread_mutex_lock和pthread_mutex_unlock加锁和解锁。注意:mutex可以通过PTHREAD_MUTEX_RECURSIVE创建递归锁,防止重复获取锁导致死锁

 //创建锁,注意:mutex可以通过PTHREAD_MUTEX_RECURSIVE创建递归锁,防止重复获取锁导致死锁
 pthread_mutexattr_t recursiveAttr;
 pthread_mutexattr_init(&recursiveAttr);
 pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE);
 pthread_mutex_init(self.mutex, &recursiveAttr);
 pthread_mutexattr_destroy(&recursiveAttr);

pthread_mutex_lock(&self.mutex)
//访问共享数据代码
pthread_mutex_unlock(&self.mutex)

OSSpinLock

OSSpinLock 是自旋锁,等待锁的线程会处于忙等状态。一直占用着 CPU。自旋锁就好比写了个 while,whil(被加锁了) ; 不断的忙等,重复这样。OSSpinLock是不安全的锁(会造成优先级反转),什么是优先级反转,举个例子:
有线程1和线程2,线程1的优先级比较高,那么cpu分配给线程1的时间就比较多,自旋锁可能发生优先级反转问题。如果优先级比较低的线程2先加锁了,紧接着线程1进来了,发现已经被加锁了,那么线程1忙等,while(未解锁); 不断的等待,由于线程1的优先级比较高,CPU就一直分配之间给线程1,就没有时间分配给线程2,就有可能导致线程2的代码就没有办法往下走,就会造成线程2没有办法解锁,所以这个锁就不安全了。苹果为了修复这个bug,OSSpinLock底层改为互斥锁实现了。
建议不要使用OSSpinLock,用os_unfair_lock来代替。

//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
//加锁
OSSpinLockLock(&lock);
//解锁
OSSpinLockUnlock(&lock);

os_unfair_lock

os_unfair_lock用于取代不安全的OSSpinLock,从iOS10开始才支持 从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等

//初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
//加锁
os_unfair_lock_lock(&lock);
//解锁
os_unfair_lock_unlock(&lock);

性能

性能从高到低排序
1、os_unfair_lock
2、OSSpinLock
3、dispatch_semaphore
4、pthread_mutex
5、NSLock
6、NSCondition
7、pthread_mutex(recursive)
8、NSRecursiveLock
9、NSConditionLock
10、@synchronized

资料推荐

如果你正在跳槽或者正准备跳槽不妨动动小手,添加一下咱们的交流群1012951431来获取一份详细的大厂面试资料为你的跳槽多添一份保障。

上一篇 下一篇

猜你喜欢

热点阅读