IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> iOS 底层探索篇 —— 多线程(一) -> 正文阅读

[移动开发]iOS 底层探索篇 —— 多线程(一)

1. 线程和进程

1.1 线程和进程的定义

线程

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

进程

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

在这里插入图片描述

1.2 线程和进程的关系

  • 地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
  • 资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的 资源是独立的。
  • 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮
  • 进程切换时,消耗的资源大效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程
  • 执行过程:每个独立的进程有一个程序运行的入口顺序执行序列和程序入口。但是 线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
  • 线程是处理器调度基本单位,但是进程不是。
  • 线程没有地址空间,线程包含在进程地址空间

2. 多线程

2.1 多线程的意义

优点

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

缺点

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

2.2 多线程的原理

  • 对于单核CPU,同一时间,CPU只能处理一条线程,即同一时间只有 1 个线程在执行,

  • iOS中的多线程同时执行的本质是 CPU具有调度的能力,能够在多个任务直接进行快速的切换,由于CPU调度线程的时间足够快,就造成了多线程的“同时”执行的效果。CPU在多个任务直接进行快速的切换的时间间隔就是时间片。

  • 如果是多核CPU就真的可以同时处理多个线程了,也就是并发。

    多线程消耗:
    请添加图片描述

2.3 多线程技术方案

方案简介语言现场生命周期使用频率
pthread一套通用的多线程api
适用于Unix/ Linux / windows等系统
跨平台,可移植
使用难度大
C程序员管理几乎不用
NSThread使用更加面向对象
简单易用,可直接操作线程对象
OC程序员管理偶尔使用
GCD旨在替代NSThread等线程技术
充分利用设备多核
C自动管理经常使用
NSOperation基于GCD
比GCD多了一些更简单实用的功能
使用更加面向对象
OC自动管理经常使用

2.3 多线程生命周期

线程的五个状态

  • 新建:主要是实例化线程对象
  • 就绪:线程对象调用start方法,将线程对象加入可调度线程池,等待CPU的调用,即调用start方法,并不会立即执行,进入就绪状态,需要等待一段时间,经CPU调度后才执行,也就是从就绪状态进入运行状态
  • 运行:CPU负责调度可调度线城市中线程的执行,在线程执行完成之前,其状态可能会在就绪和运行之间来回切换,这个变化是由CPU负责,开发人员不能干预。
  • 阻塞:当满足某个预定条件时,可以使用休眠,即sleep,或者同步锁,阻塞线程执行。当sleep到时,会获取同步锁重新将线程加入可调度线程池中。
  • 死亡:分为两种情况 1. 正常死亡:即线程执行完毕。2. 非正常死亡:当满足某个条件后,在线程内部(或者主线程中)终止执行(调用exit方法等退出)

在这里插入图片描述

2.4 线程池原理

请添加图片描述
饱和策略

  • AbortPolicy 直接抛出RejectedExecutionExeception异常来阻止系统正常运行
  • CallerRunsPolicy 将任务回退到调用者
  • DisOldestPolicy 丢掉等待最久的任务
  • DisCardPolicy 直接丢弃任务
    这四种拒绝策略均实现了RejectedExecutionHandler接口

3. 面试题

3.1 任务的执行速度的隐形因素

  • CPU
  • 任务复杂度
  • 优先级
  • 线程状态

3.2 优先级反转

在看优先级反转前先了解什么是IO密集型线程和CPU密集型线程。

  • IO密集型线程:频繁等待的线程,等待的时候会让出时间片。
  • CPU密集型线程:很少等待的线程,意味着长时间占用着 CPU。

IO密集型线程比CPU密集型线程更容易得到优先级提升。
特殊场景下,当多个优先级都比较高的CPU 密集型线程霸占了所有 CPU 资源,而此时优先级较低的 IO 密集型线程将持续等待,产生线程饿死的现象。当然,为了避免线程饿死,CPU会发挥调度作用去逐步提高被“冷落”线程的优先级(提高优先级不一定会立即执行),IO 密集型线程通常情况下比 CPU 密集型线程更容易获取到优先级提升。

线程的优先级影响因素:

  • 用户指定
  • 等待的频繁度
  • 不执行

用户指定线程优先级:
在这里插入图片描述
在这里插入图片描述

4. 线程安全问题

多线程操作过程中往往多个线程是并发执行的,同一个资源可能被多个线程同时访问,造成资源抢夺,这个过程中如果没有锁机制往往会造成重大问题。

IOS主要有两种锁:

  • 互斥锁
  • 自旋锁 :当发现其他线程执行,当前线程询问,忙等,消耗性能比较高。任务短的时候用自旋锁,atomic的实现就用的自旋锁。

互斥锁:

  • 保证锁内的代码,同一时间,只有一条线程能够执行。
  • 互斥锁的锁定范围,应该尽量小,锁定范围越大,效率越差。
  • 当发现其他线程执行,当前线程休眠(就绪状态),一直在等打开,唤醒执行。
    互斥锁参数
  • 能够加锁的任意 NSObject 对象
  • 锁对象一定要保证所有的线程都能够访问
  • 如果代码中只有一个地方需要加锁,大多都使用 self,这样可以避免单独再创建一个
    锁对象

自旋锁:

  • 当发现其他线程执行,当前线程询问,忙等,消耗性能比较高。任务短的时候用自旋锁,atomic给setter 和getter 加锁加的就是自旋锁。

5. GCD

GCD 全称是 Grand Central Dispatch,主要做的是将任务添加到队列,并且指定执行任务的函数。纯 C 语言,提供了非常多强大的函数,。

  • GCD 是苹果公司为多核的并行运算提出的解决方案
  • GCD 会自动利用更多的CPU内核(比如双核、四核)
  • GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
  • 程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码

5.1 同步 & 异步

  • 异步 dispatch_async:不用等待当前语句执行完毕,就可以执行下一条语句,会开启线程执行 block 的任务,异步是多线程的代名词。异步也是按顺序执行多项任务,但是是放在多个线程里同时运行,执行结束的顺序是随机的、不可预估的。总耗时大约是耗时最长的那项任务所消耗的时间。
  • 同步 dispatch_sync: 必须等待当前语句执行完毕,才会执行下一条语句,不会开启线程,在当前执行 block 的任务。执行结束的顺序是固定的、和任务的执行顺序相同。总耗时是所有任务耗时之和。

5.2 串行 & 并行

  • 串行:多个任务时,各个任务按顺序执行,完成一个之后才能进行下一个,遵守FIFO。
  • 并行:指的是多个任务可以同时执行。
    在这里插入图片描述

5.3 同步异步耗时对比

这里耗时相差大的原因是,同步会阻塞当前线程,先执行testMehod()之后才执行NSLOG()。而异步则大概率先NSLog在执行testMehod()。
在这里插入图片描述
在这里插入图片描述

面试题

下列打印顺序是怎么样的呢?
这里是并行队列,大概率的打印顺序是 1 - 5 - 2 - 3 - 4,也可能是 1 - 2 - 3 - 4 - 5。因为async不会阻塞当前线程,那么 2 和 5的打印顺序就不确定, 而 3 是sync,阻塞当前线程,那么就一定会在4之前打印。
在这里插入图片描述
这个代码会死锁,为什么呢?因为这里是串行队列, dispatch_sync等待dispatch_async执行完,才会执行,而dispatch_async则被dispatch_sync堵塞住,执行不完,这样就造成了死锁。
在这里插入图片描述

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-03 11:19:29  更:2021-08-03 11:19:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 5:05:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码