美文网首页
iOS - 多线程

iOS - 多线程

作者: Gumball_a45f | 来源:发表于2021-04-01 16:25 被阅读0次

1.线程与进程的关系

线程:

  • 线程是进程的基本执行单元,一个进程的所有任务都在线程中执行
  • 进程要向执行任务,必须得有进程,进程至少要有一条线程
  • 程序启动会默认开启一条线程,这条线程被称为主线程UI线程

进程:

  • 进程是指在系统中正在运行的一个应用程序
  • 每个进程之间是独立的,每个进程均运行在其专用的且受保护内存空间内
  • 通过“活动监视器”可以查看Mac系统中所开启的进程

地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
资源拥有:同一进程的线程共享本进程的资源,而进程之间则是独立的资源。

2.多线程

优点:

  1. 能适当提高程序的执行效率
  2. 能适当提高资源的利用率(CPU,内存)
  3. 线程上的任务执行完成后线程会自动销毁

缺点:

  1. 开启线程需要占用一定的内存空间(默认情况下,每一个线程都占512KB
  2. 如果开启大量的线程,会占用大量的内存空间,降低程序的性能
  3. 线程越多,CPU在调用线程上的开销就越大
  4. 程序设计更加复杂,比如线程间的通信、多线程的数据共享

iOS 的多线程其实就是CPU快速的在多个线程之间快速的切换,造成了多线程同事执行的效果。
如果线程数非常多就会消耗大量的CPU资源,导致线程执行效率降低。

3.多线程生命周期(掌握)

先来看一张图:引用Style_月月

从图中可以看出多线程的生命周期分为:
新建 -> 就绪 -> 运行 -> 阻塞 -> 死亡 -> 就绪
    |________________|

  • 新建:实例化线程对象

  • 就绪:线程调用start方法,将线程加入可调度线程池,等待CPU的调用。不会立刻执行,当CPU调度到该线程时状态由就是转为运行

  • 运行:CPU负责调度可调度线程池中线程的执行,在线程执行完成之前,其状态可能会在就绪和运行之间来回切换,这个变化是由CPU负责,开发人员不能干预。

  • 阻塞:当满足某个预定条件时,可以使用休眠,即 sleep,或者同步锁,阻塞线程执行。当进入sleep时,会重新将线程加入就绪中。下面关于休眠的时间设置,都是NSThread的

  • 死亡:正常死亡,即线程执行完毕;非正常死亡,调用exit方法等退出

4.线程和Runloop的关系

  1. runloop与线程是⼀⼀对应的,⼀个runloop对应⼀个核⼼的线程,为什么说是核⼼的,是因为runloop是可以嵌套的,但是核⼼的只能有⼀个,他们的关系保存在⼀个全局的字典⾥。
  2. runloop是来管理线程的,当线程的runloop被开启后,线程会在执⾏完任务后进⼊休眠状态,有了任务就会被唤醒去执⾏任务。
  3. runloop在第⼀次获取时被创建,在线程结束时被销毁。
  4. 对于主线程来说,runloop在程序⼀启动就默认创建好了
  5. 对于⼦线程来说,runloop是懒加载的,只有当我们使⽤的时候才会创建,所以在⼦线程⽤定时器要注意:确保⼦线程的runloop被创建,不然定时器不会回调。

线程的exitcancel说明

  • exit:强行终止线程,后续的所有代码不执行
  • cancel:取消当前线程,但是不能取消正在执行的线程

面试:线程的优先级越高,任务执行越快?
答: 否,线程执行的快慢,取决于任务的复杂程度,以及CPU的调度情况。

5.线程池(掌握)

线程池运行流程如下图
  1. 核心线程是否都在执行任务
  • NO, 创建新的线程去执行
  • YES, --->2
  1. 线程池工作列队是否饱满
  • NO,将任务储存在工作队列
  • YES, --->3
  1. 判断线程池中的线程是否都处于执行状态
  • NO,安排线程去执行任务
  • YES, --->4
  1. 交给饱和策略去执行

饱和策略

  • AbortPolicy 直接抛出RejectedExecutionExeception异常来阻⽌系统正常运⾏
  • CallerRunsPolicy 将任务回退到调⽤者
  • DisOldestPolicy 丢掉等待最久的任务
  • DisCardPolicy 直接丢弃任务

这四种拒绝策略均实现的RejectedExecutionHandler接⼝

iOS中多线程的实现方案

6.线程安全问题

多线程会可能触发抢夺资源的问题(数据安全问题)
iOS提供了两种解决方案(当然还有其他的方法,递归锁、条件锁..)

互斥锁

  • 互斥锁@synchronized

    • 保证锁内的代码,同⼀时间,只有⼀条线程能够执⾏!如果发现其他线程正在执行锁定的代码,新线程就会进入休眠
    • 互斥锁的锁定范围,应该尽量⼩,锁定范围越⼤,效率越差!

    互斥锁参数

    • 能够加锁的任意 NSObject 对象
    • 注意:锁对象⼀定要保证所有的线程都能够访问
    • 如果代码中只有⼀个地⽅需要加锁,⼤多都使⽤ self,这样可以避免单独再创建⼀个锁对象

自旋锁

  • 自旋锁与互斥锁类似,但它是在获取锁之前一直处于忙等(即原地打转,称为自旋)阻塞状态

  • 使用场景:锁持有的时间短,就需要使用自旋锁,属性修饰符atomic,本身就有一把自旋锁

atomic与nonatomic 的区别

nonatomic ⾮原⼦属性
atomic 原⼦属性(线程安全),针对多线程设计的,默认值

  • 保证同⼀时间只有⼀个线程能够写⼊(但是同⼀个时间多个线程都可以取值)
  • atomic 本身就有⼀把锁(⾃旋锁)
  • 单写多读:单个线程写⼊,多个线程可以读取

atomic:线程安全,需要消耗⼤量的资源
nonatomic:⾮线程安全,适合内存⼩的移动设备

iOS 开发的建议
所有属性都声明为 nonatomic
尽量避免多线程抢夺同⼀块资源
尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减⼩移动客户端的压⼒

相关文章

网友评论

      本文标题:iOS - 多线程

      本文链接:https://www.haomeiwen.com/subject/wsylhltx.html