多线程相关面试问题

内部创建了一个pthread线程,当main函数或者说指定的target-selector执行结束之后,系统会为线程做退出管理操作,如果想实现一个常驻线程的话,就需要我们在NSThread的target的selector方法中维护一个事件循环

image

一、GCD

1、同步串行

2、同步并发

3、异步串行

4、异步并发

image

GCD 底层创建的线程没有默认不开启 runloop,所以 performSelector 的任务所在线程没有对应的 runloop,所以无法执行

5、dispatch_barrier_async()

有难度的问法:如何实现多读单写?
简单点的问法:如何通过 GCD 实现多读单写?

解决方案:

image

image

image

6、dispatch_group_async()

image

image
image

临时想到的问题:

  • 比如通过 **SDWebImage** 下载图片,本身SDWebImage的下载又是异步的,下载完成后再回到SD自己的回调,怎么记录每个sd的下载好的图片,在notify中使用?
  • dispatch_group还有其他什么用途?

二、NSOperation

image

  • 任务执行状态

    image

    image

    image

    image

    image

    image

    imageimage

    引申出一道面试题

    image

  • 自我扩展:如何给任务添加依赖以及要点和注意点?

  • 最大并发量的使用要点和注意点?

三、NSThread

image

主要考察 NSThread 也就两个

  • 如何通过 NSThread 结合 Runloop 实现一个常驻线程?
  • NSThread 的内部实现机制是怎样的?

这两个问题回答如下:

内部创建了一个pthread线程,当main函数或者说指定的target-selector执行结束之后,系统会为线程做退出管理操作,如果想实现一个常驻线程的话,就需要我们在NSThread的target的selector方法中维护一个事件循环

image

image

image

image

image

由此可见,如果不在 main​函数中添加一个 Runlloop​,线程就会 立马销毁

image

target 和 selector 是如何被传到这个线程中的?
  • image
  • image

NSThread​​ 的 执行原理是什么样的?

四、多线程与琐

iOS 中都有哪些琐? ​​

@synchronized

一般在 创建单例对象 的时候使用,来保证在多线程环境下创建单例对象是唯一的

atomic

image

image

OSSpinLock(已废弃, 已被替换为 os_unfair_lock)

image

image
image

谈谈你对 OSSpinLock 的理解

满分回答:

  • 自旋锁,循环等待访问,并不释放当前资源
  • 用于轻量级数据访问,简单的 int 值 +1/-1 操作
  • 在看 runtime 源码内存管理的时候,发现引用计数就是通过此方案实现的

os_unfair_lock

CleanShot 2025-04-21 at 03.30.14@2x

NSLock

一般用于解决细粒度的线程同步问题,保证各个线程互斥进入临界区

image

  • 当已获取锁再去获取锁时,就会因为 **重入** 的问题导致死锁
  • 通过 递归锁 NSRecursiveLock 来解决

NSRecursiveLock

image

应用场景:

  • 递归场景需要加锁

NSCondition 和 NSConditionLock(条件锁

  • NSCondition

    CleanShot 2025-04-21 at 03.24.30@2x

  • NSConditionLock

    CleanShot 2025-04-21 at 03.25.59@2x

互斥锁 (​pthread_mutex)

CleanShot 2025-04-21 at 03.15.17@2x

dispatch_semaphore_t

image

原理分析:

image

image

image

五、多线程面试问题总结

  • 怎样用 GCD 多读单写?

    • 解决方案:

      并发队列,读时同步读取,写时用异步栅栏函数

    • image

    • image

    • image

  • iOS 系统为我们提供的几种多线程技术?各自的特点是怎么样的?

    • GCD、NSOperation 和 NSOperationQueue、NSThread

    • 各自的特点

      • GCD

        • 特点:

          • 基于 C 语言的底层 API:高性能,充分利用多核处理器

          • 队列驱动模型:

            • 队列类型: 串行队列(顺序执行)、并发队列(并行执行)、主队列(UI 线程专用)。
            • 任务派发方式: dispatch_sync(同步阻塞)、dispatch_async(异步非阻塞)
          • 自动线程管理:系统自动分配线程资源,开发者无需关注线程细节

          • 高级功能

            • 栅栏(Barrier) :确保写操作独占队列,解决读写竞争
            • 组(Group) :协调多个任务的完成状态
          • 缺点:任务复杂的依赖关系需手动实现,灵活性较低

        • 应用场景:

          • 高并发任务:如图片下载

          • 简单的线程同步:

            • 开辟子线程执行任务
            • 异步耗时操作完成后异步回主线程操作 UI
            • 利用异步栅栏函数实现多读单写功能
            • 通过队列组实现多个异步任务完成后通过 group 进行回调统一处理逻辑,比如我们的正文页生成海报分享,需要下载多个图片然后统一绘制海报,如何拼接多个图片和文字
      • NSOperation 和 NSOperationQueue

        • 特点:

          • 面向对象抽象:基于 GCD 封装,提供更高层级的任务管理

          • 任务控制能力

            • 添加、移除任务之间依赖顺序

            • 优先级控制:通过 queuePriority​调整任务执行顺序

            • 控制任务执行状态,如重写 main 或 start 方法,取消与暂停任务等等

              image

            • 控制最大并发量

          • 线程池管理: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); }); }
  • 线程
    123 引用 • 111 回帖 • 3 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...