内部创建了一个pthread线程,当main函数或者说指定的target-selector执行结束之后,系统会为线程做退出管理操作,如果想实现一个常驻线程的话,就需要我们在NSThread的target的selector方法中维护一个事件循环
一、GCD
1、同步串行
2、同步并发
3、异步串行
4、异步并发
GCD 底层创建的线程没有默认不开启 runloop,所以 performSelector 的任务所在线程没有对应的 runloop,所以无法执行
5、dispatch_barrier_async()
有难度的问法:如何实现多读单写?
简单点的问法:如何通过 GCD 实现多读单写?
解决方案:
6、dispatch_group_async()
临时想到的问题:
- 比如通过 **SDWebImage** 下载图片,本身SDWebImage的下载又是异步的,下载完成后再回到SD自己的回调,怎么记录每个sd的下载好的图片,在notify中使用?
- dispatch_group还有其他什么用途?
二、NSOperation
-
任务执行状态
引申出一道面试题
-
自我扩展:如何给任务添加依赖以及要点和注意点?
-
最大并发量的使用要点和注意点?
三、NSThread
主要考察 NSThread 也就两个
- 如何通过 NSThread 结合 Runloop 实现一个常驻线程?
- NSThread 的内部实现机制是怎样的?
这两个问题回答如下:
内部创建了一个pthread线程,当main函数或者说指定的target-selector执行结束之后,系统会为线程做退出管理操作,如果想实现一个常驻线程的话,就需要我们在NSThread的target的selector方法中维护一个事件循环
由此可见,如果不在 main
函数中添加一个 Runlloop
,线程就会 立马销毁
NSThread
的 执行原理是什么样的?
四、多线程与琐
iOS 中都有哪些琐?
@synchronized
一般在 创建单例对象 的时候使用,来保证在多线程环境下创建单例对象是唯一的
atomic
OSSpinLock(已废弃, 已被替换为 os_unfair_lock)
谈谈你对 OSSpinLock 的理解
满分回答:
- 自旋锁,循环等待访问,并不释放当前资源
- 用于轻量级数据访问,简单的 int 值 +1/-1 操作
- 在看 runtime 源码内存管理的时候,发现引用计数就是通过此方案实现的
os_unfair_lock
NSLock
一般用于解决细粒度的线程同步问题,保证各个线程互斥进入临界区
- 当已获取锁再去获取锁时,就会因为 **重入** 的问题导致死锁
- 通过 递归锁 NSRecursiveLock 来解决
NSRecursiveLock
应用场景:
- 递归场景需要加锁
NSCondition 和 NSConditionLock(条件锁)
-
NSCondition
-
NSConditionLock
互斥锁 (pthread_mutex)
dispatch_semaphore_t
原理分析:
五、多线程面试问题总结
-
怎样用 GCD 多读单写?
-
解决方案:
并发队列,读时同步读取,写时用异步栅栏函数
-
-
-
-
-
iOS 系统为我们提供的几种多线程技术?各自的特点是怎么样的?
-
GCD、NSOperation 和 NSOperationQueue、NSThread
-
各自的特点
-
GCD
-
特点:
-
基于 C 语言的底层 API:高性能,充分利用多核处理器
-
队列驱动模型:
- 队列类型: 串行队列(顺序执行)、并发队列(并行执行)、主队列(UI 线程专用)。
- 任务派发方式: dispatch_sync(同步阻塞)、dispatch_async(异步非阻塞)
-
自动线程管理:系统自动分配线程资源,开发者无需关注线程细节
-
高级功能:
- 栅栏(Barrier) :确保写操作独占队列,解决读写竞争
- 组(Group) :协调多个任务的完成状态
-
缺点:任务复杂的依赖关系需手动实现,灵活性较低
-
-
应用场景:
-
高并发任务:如图片下载
-
简单的线程同步:
- 开辟子线程执行任务
- 异步耗时操作完成后异步回主线程操作 UI
- 利用异步栅栏函数实现多读单写功能
- 通过队列组实现多个异步任务完成后通过 group 进行回调统一处理逻辑,比如我们的正文页生成海报分享,需要下载多个图片然后统一绘制海报,如何拼接多个图片和文字
-
-
-
NSOperation 和 NSOperationQueue
-
特点:
-
面向对象抽象:基于 GCD 封装,提供更高层级的任务管理
-
任务控制能力:
-
添加、移除任务之间依赖顺序
-
优先级控制:通过
queuePriority
调整任务执行顺序 -
控制任务执行状态,如重写 main 或 start 方法,取消与暂停任务等等
-
控制最大并发量
-
-
线程池管理:NSOperationQueue 自动管理线程数量和复用
-
缺点:相比 GCD 略有性能开销
-
-
应用场景:
- 复杂任务调度(如批量下载、数据处理流水线),比如第三方框架的 AFNetworking、SDWebImage 等都用到了
-
-
NSThread
-
特点:
- 轻量级线程管理:每个线程对应一个 NSThread 对象,适合简单任务。
- 显式线程控制:需手动管理线程的启动、暂停、取消及同步,易引发资源竞争或死锁。
- 与 RunLoop 结合:可通过
performSelector
系列方法实现线程间通信,也可以实现线程保活等
-
应用场景:
- 简单任务
- 调试多线程问题(如使用[NSThread currentThread]跟踪线程)
- 结合 Runloop 实现常驻线程(线程保活)、
performSelector
实现线程同步
-
-
-
-
NSOperation 对象在 Finish 之后怎样从 queue 中移除的?
内部完成之后,会通过 KVO 改变 isExecuting 和 isFinished 状态,queue 可以监听到状态的改变,从而将 NSOperation 对象移除
-
你都用过哪些锁?结合实际谈谈你是怎样使用的?
-
NSLock:
-
特点:
- 基础互斥锁,不可重入
-
使用场景:
- 保护简单的临界区,简单加锁场景,无递归无重入场景的时候
-
-
NSRecursiveLock:
-
特点:允许同一线程多次加锁,解决递归调用死锁问题
-
使用场景:
- 递归函数或嵌套方法链,如递归查找导航栈顶控制器
-
-
@synchronized
-
特点:隐式递归锁,自动异常处理,性能较差但易用性高
-
使用场景:
-
快速保护代码块,如单例初始化
-
临时资源操作:在数据缓存工具类中保护临时缓存操作
- (void)cacheData:(NSData *)data forKey:(NSString *)key { @synchronized (self.cache) { [self.cache setObject:data forKey:key]; } }
-
-
-
dispatch_semaphore_t 信号量
-
特点: 通过计数控制并发数,性能优异,耗时是纳秒级的
-
使用场景:限制资源并发访问量,如线程池或网络请求节流,案例如下
// 限制最大并发数为4 dispatch_semaphore_t semaphore = dispatch_semaphore_create(4); - (void)downloadImageWithURL:(NSURL *)url { dispatch_async(dispatch_get_global_queue(0, 0), ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 执行下载 dispatch_semaphore_signal(semaphore); }); }
-
-
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于