【iOS】进程与多线程

news2024/11/15 14:08:57

目录

    • 前言
    • 进程和线程
      • 进程和线程的区别
      • 多线程的意义
      • 时间片概念
    • 线程的生命周期
    • 线程池的运行策略
    • 自旋锁和互斥锁
      • 自旋锁
      • 互斥锁
      • 自旋锁和互斥锁区别
      • 原子属性
    • iOS多线程技术方案


前言

学习此文:iOS多线程
在平时的iOS开发中,多线程是我们常会遇到的,开启新线程,比如pthread、NSThread、GCD、NSOperation,其中GCD、NSOperation是我们最常用。在研究这些之前,我们先来了解一些多线程方面的概念
iOS开发是单进程,Android可以是多进程

进程和线程

什么是进程

  • 进程是指在系统中正在运⾏的⼀个应⽤程序,它是程序执行时的一个实例
  • 程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行
  • 每个进程之间是独立的,每个进程运行在其专有的且受保护的内存空间内
  • 在 Mac电脑上,可以通过“活动监视器”查看所开启的进程

. . . . 在这里插入图片描述

什么是线程

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

进程和线程的区别

  • 进程是资源分配的最小单位,线程是程序执行的最小单位
  • 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换或创建一个线程的花销远比进程要小很多
  • 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点
  • 但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间

多线程的意义

NSLog(@"开始");
NSInteger count = 1000 * 100;
for (NSInteger i = 0; i < count; i++) {
    // 栈区
    NSInteger num = i;
    // 常量区
    NSString *name = @"RENO";
    // 堆区
    NSString *myName = [NSString stringWithFormat:@"%@ - %zd", name, num];
    NSLog(@"%@", myName);
}
NSLog(@"结束");

上面的代码中,执行了10万次循环,每次循环都会创建局部变量,此过程执行完成耗时10秒,如果此流程放在主线程,会造成主线程卡顿,极大影响用户体验

所以通常情况下,我们都会进行异步处理,开启新的线程对这些事务进行处理,而如果一个事务很复杂,比较耗时,可以将一个大的事务拆分成多个小的事务进行并发处理,这样可以节省时间,并且不会影响用户的体验

多线程的优缺点

相较单线程

优点:

  • 提高程序的执行效率
  • 提高资源的利用率(如CPU、内存)
  • 线程上人物执行完毕后,线程会自动销毁

缺点:

  • 开启线程需要占⽤⼀定的内存空间(默认情况下,iOS主线程占用1M,子线程占512KB),如果开启⼤量的线程,CPU在调⽤线程上的开销就越⼤,会占⽤⼤量的内存空间,降低程序的性能
  • 程序设计更加复杂,⽐如线程间的通信、多线程的数据共享

时间片概念

开启过多的线程也会导致性能的下降,这里涉及到时间片的概念。多线程的执行是CPU快速的在多个线程之间进行切换。线程数过多,CPU会在多个线程之间切换,创建和销毁大量的CPU资源,反而导致执行效率的下降

  • 时间⽚的概念:CPU在多个任务直接进⾏快速的切换,这个切换的时间间隔就是时间⽚。(单核CPU)同⼀时间,CPU只能处理1个线程,换⾔之,同⼀时间只有 1 个线程在执⾏
  • 多线程同时执⾏:是CPU快速的在多个线程之间的切换,CPU调度线程的时间⾜够快,就造成了多线程的同时执⾏的效果
  • 如果线程数⾮常多,CPU会在N个线程之间切换,消耗⼤量的CPU资源,每个线程被调度的次数会降低,线程的执⾏效率降低

理解:

多线程程序可以在多个线程之间反复多次进行上下文切换,宏观上看上去就好像单个CPU核能够并列执行多个线程一样。而且在多核CPU的情况下,就是真的在并行执行多个线程

线程的生命周期

App有生命周期,那么线程的生命周期是什么样子的呢?

线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁。见下图:

请添加图片描述

  • 新建:通过创建线程的函数,创建一个新线程
  • 就绪:调用线程的start方法,这时线程处于等待CPU分配资源阶段,谁先抢占CPU资源,谁开始执行
  • 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能
  • 阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep、等待同步锁,线程就从可调度线程池移出,处于了阻塞状态,这个时候sleep到时、获取同步锁,此时会重新添加到可调度线程池。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态
  • 销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源

线程池的运行策略

运行策略

在这里插入图片描述

队列满且正在运行的线程数量小于最大线程数,则新进入的任务,会直接创建非核心线程工作

  • 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们
  • 当有任务时,线程池会做如下判断:
    • 如果正在运行的线程数量小于corePoolSize(核心线程数),那么马上创建核心线程运行这个任务
    • 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列
    • 如果队列满了,而且正在运行的线程数量小于maximumPoolSize(最大线程数),那么还是要创建非核心线程立刻运行这个任务
    • 如果队列满了,而且正在运行的线程数量大于或等于maximumPoolSize,那么线程池饱和策略将进行处理
  • 当一个线程完成任务时,它会从队列中取下一个任务来执行
  • 当一个线程无事可做,超过一定的时间(超时)时,线程池会判断,如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小

饱和策略

如果线程池中的队列满了,并且正在运行的线程数量已经大于等于当前线程池的最大线程数,则进行饱和策略的处理

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

自旋锁和互斥锁

锁作为一种非强制的机制,被用来保证线程安全。每一个线程在访问数据或者资源前,要先获取(Acquire)锁,并在访问结束之后释放(Release)锁。如果锁已经被占用,其它试图获取锁的线程会等待,直到锁重新可用
注:不要将过多的其他操作代码放到锁里面,否则一个线程执行的时候另一个线程就一直在等待,就无法发挥多线程的作用了

自旋锁

使用一种用于用于保护多线程共享资源的锁,当自旋锁尝试获取锁时以忙等待(busy waiting,线程在这一过程中保持执行)的形式不断地循环检查锁是否可用

当上一个线程的任务没有执行完毕的时候(被锁住),那么下一个线程会一直等待(不会睡眠),当上一个线程的任务执行完毕,下一个线程会立即执行

自旋锁:OSSpinLock、dispatch_semaphore_t

互斥锁

当上一个线程的任务没有执行完毕的时候(被锁住),那么下一个线程会进入睡眠状态等待任务执行完毕,当上一个线程的任务执行完毕,下一个线程会自动唤醒然后执行任务,该任务也不会立刻执行,而是成为可执行状态(就绪,Runnable)

互斥锁:pthread_mutex、@ synchronized、NSLock、NSConditionLock、NSCondition、NSRecursiveLock

自旋锁和互斥锁区别

  • 自旋锁会忙等,所谓忙等,即在访问被锁资源时,调用者线程不会休眠,而是不停循环在那里,直到被锁资源显式释放锁
  • 互斥锁会休眠,所谓休眠,即在访问被锁资源时,调用者线程会休眠,此时CPU可以调度其他线程工作,直到 被锁资源 释放锁。此时会唤醒休眠线程
  • 优缺点
    • 优点:因为自旋锁不会引起调用者睡眠,所以不会进行线程调度、CPU时间片轮转等耗时操作。所以如果能在很短的时间内获得锁,自旋锁的效率远高于互斥锁
    • 缺点:自旋锁一直占用CPU,他在未获得锁的情况下,一直运行自旋,所以占用着CPU,如果不能在很短的时间内获得锁,这无疑会使CPU效率降低。自旋锁不能实现递归调用

原子属性

  • OC在定义属性时有nonatomic和atomic两种选择,默认为atomic属性
    • atomic:原子属性,底层是通过Spinlock为setter方法加自旋锁(即为单写set多读get)
    • nonatomic:非原子属性,不会为setter方法加锁
  • nonatomic和atomic的对比
    • atomic:线程安全,需要消耗大量的资源
    • nonatomic:非线程安全,适合内存小的移动设备
  • iOS开发建议
    • 如非需抢占资源的属性(如购票,充值),所有属性都声明为nonatomic
    • 尽量避免多线程抢夺同一块资源
    • 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力
  • atomic底层原理
    属性setter方法底层调用的是objc_setProperty函数,其会调用reallySetProperty函数,该函数的实现中,针对原子属性,添加了spinlock锁:
    在这里插入图片描述
    在这里插入图片描述

    Spinlock是Linux内核中提供的一种比较常见的锁机制,自旋锁是原地等待的方式解决资源冲突的,即一个线程获取了一个自旋锁后,另外一个线程期望获取该自旋锁,获取不到,只能够原地打转(忙等待)。由于自旋锁的这个忙等待的特性,注定了它使用场景上的限制 —— 自旋锁不应该被长时间的持有(消耗CPU资源)

iOS多线程技术方案

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1944379.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EasyMedia转码rtsp视频流flv格式,hls格式,H5页面播放flv流视频

EasyMedia转码rtsp视频流flv格式&#xff0c;hls格式 H5页面播放flv流视频 文章最后有源码地址 解决海康视频播放视频流&#xff0c;先转码后自定义页面播放flv视频流 先看效果&#xff0c;1&#xff0c;EasyMedia自带的页面&#xff0c;这个页面二次开发改动页面比较麻烦 …

WARNING: The Nouveau kernel driver is currently in use by your system. 处理方法

实践系统&#xff1a; 安装NVIDIA驱动时&#xff0c;提示&#xff1a; WARNING: The Nouveau kernel driver is currently in use by your system. This driver is incompatible with the NVIDIA driver&#xff0c;and must be disabled before proceeding.警告&#xff1…

【分布式锁】Redis实现分布式锁

在分布式系统中&#xff0c;当多个服务实例&#xff08;或节点&#xff09;需要访问或修改同一份共享资源时&#xff0c;就需要使用分布式锁来确保数据的一致性和防止并发问题。这种情况下&#xff0c;传统的Java并发控制机制如ReentrantLock或synchronized就无法满足需求&…

.NET开源、简单、实用的数据库文档生成工具

前言 今天大姚给大家分享一款.NET开源&#xff08;MIT License&#xff09;、免费、简单、实用的数据库文档&#xff08;字典&#xff09;生成工具&#xff0c;该工具支持CHM、Word、Excel、PDF、Html、XML、Markdown等多文档格式的导出&#xff1a;DBCHM。 支持的数据库 Sq…

Docker+consul容器服务的更新与发现

1、Consul概述 &#xff08;1&#xff09;什么是服务注册与发现 服务注册与发现是微服务架构中不可或缺的重要组件。起初服务都是单节点的&#xff0c;不保障高可用性&#xff0c;也不考虑服务的压力承载&#xff0c;服务之间调用单纯的通过接口访问。直到后来出现了多个节点…

初学MySQl简单sql语句(1)

目录 SQL语句介绍&#xff1a; DDL创建数据库&#xff1a; char和varchar比较 数值类型 数据库存储引擎 数据库存储引擎——InnoDB 数据库存储引擎——MyISAM 数据库存储引擎-MyISAM 和InnoDB区别 修改和删除数据库表 数据库设计三大范式 一、什么是范式 二、约束作…

如何批量重命名文件名?批量快速自定义文件名称怎么操作?

如何批量重命名文件名&#xff1f;批量快速自定义文件名称怎么操作&#xff1f;在高效率的信息化时代&#xff0c;呆板的工作方式已经不能满足我们的时效性&#xff0c;很多场景都需要一个高效率的方法来提升我们的工作效率&#xff0c;来完成任务的进度&#xff01; 如果用手…

redis的持久化机制以及集群模式

1.redis的持久化机制 内存数据库具有高速读写的优势&#xff0c;但由于数据存储在内存中&#xff0c;一旦服务器停止或崩溃&#xff0c;所有数据将会丢失。持久化机制的引入旨在将内存中的数据持久化到磁盘上&#xff0c;从而在服务器重启后能够恢复数据&#xff0c;提供更好的…

Python异常处理机制、调试模式

一、Bug的由来 1、Bug的由来 世界上第一部万用计算机的进化版-马克2号(Mark II) 2、Debug 二、Bug的分类 和 不同异常类型的处理方式 1、粗心导致的语法错误 SyntaxError &#xff08;1&#xff09; # age input(请输入你的年龄) # if age > 18: # print(成年人..…

深入浅出mediasoup—通信框架

libuv 是一个跨平台的异步事件驱动库&#xff0c;用于构建高性能和可扩展的网络应用程序。mediasoup 基于 libuv 构建了包括管道、信号和 socket 在内的一整套通信框架&#xff0c;具有单线程、事件驱动和异步的典型特征&#xff0c;是构建高性能 WebRTC 流媒体服务器的重要基础…

华为AR6300S路由器开启SSH远程登录

登录华为路由器&#xff1a; 使用控制台线连接到路由器的控制台端口或者通过Telnet或Web界面远程登录到设备。进入系统视图&#xff1a; 输入system-view&#xff08;或者简写为sys&#xff09;命令进入系统视图模式&#xff0c;这是配置全局参数的地方。生成RSA密钥对&#x…

dockerfile部署wordpress

1.将容器直接提交成镜像 [rootlocalhost ~]# docker commit 8ecc7f6b9c12 nginx:1.1 sha256:9a2bb94ba6d8d952527df616febf3fbc8f842b3b9e28b7011b50c743cd7b233b [rootlocalhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx …

昇思25天学习打卡营第22天 | DCGAN生成漫画头像

昇思25天学习打卡营第22天 | DCGAN生成漫画头像 文章目录 昇思25天学习打卡营第22天 | DCGAN生成漫画头像DCGAN模型数据集数据下载和超参数创建数据集数据集可视化 搭建网络生成器判别器损失函数和优化器 模型训练总结打卡 DCGAN模型 深度卷积对抗生成网络&#xff08;Depp Co…

【区块链+绿色低碳】巴中市生态价值核算创新应用 | FISCO BCOS应用案例

生态产品总值&#xff08;GEP&#xff09;&#xff0c;指一定区域生态系统为人类福祉和经济社会可持续发展提供的产品与服务价值总和&#xff0c;包 括供给产品价值、调节服务价值和文化服务价值。当前&#xff0c;推动生态产品价值有效转化存在“难度量、难抵押、难交易、 难变…

nodejs启动项目报错 Error: listen EACCES: permission denied 0.0.0.0:5000

nodejs启动项目报错 Error: listen EACCES: permission denied 0.0.0.0:5000&#xff0c;截图如下&#xff1a; 解决方法 在管理员权限下打开 CMD&#xff08;命令行&#xff09;并运行&#xff1a; net stop winnatnet start winnat 执行完成后在此通过nodejs启动项目即可…

【SpringCloud】 微服务分布式环境下的事务问题,seata大合集

目录 微服务分布式环境下的事务问题 分布式事务 本地事务 BASE理论与强弱一致性 BASE理论 强弱一致性 常见分布式事务解决方案 - 2PC 常见分布式事务解决方案 - TCC 常见分布式事务解决方案 - 最大努力通知 常见分布式事务解决方案 - 最终一致性 Seata介绍与术语 Seata…

UE4-字体导入

一.字体导入 方法一&#xff1a; 然后通过导入将自己想要的字体导入到项目中&#xff0c;也可以直接将我们放在桌面的字体直接拖入到我们的内容浏览器中。 但是要注意想要发售游戏的话不可以这样导入微软的字体&#xff0c;因为Windows自带基本都有版权&#xff0c;所以最…

windows10 安装CUDA教程

如何在windows10系统上安装CUDA? 1、查看电脑的NVIDIA版本 nvidia-smi 2、官网下载所需CUDA版本 官网地址:https://developer.nvidia.com/cuda-toolkit-archive 我们所安装的CUDA版本需要小于等于本机电脑的NVIDIA版本。推荐使用迅雷下载,速度会更快哦。 3、安装步骤

【C++高阶】深度剖析:从零开始模拟实现 unordered 的奥秘

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;哈希底层 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀哈希 &#x1f4d2;1. 改造 HashTable…

C++学习笔记04-补充知识点(问题-解答自查版)

前言 以下问题以Q&A形式记录&#xff0c;基本上都是笔者在初学一轮后&#xff0c;掌握不牢或者频繁忘记的点 Q&A的形式有助于学习过程中时刻关注自己的输入与输出关系&#xff0c;也适合做查漏补缺和复盘。 本文对读者可以用作自查&#xff0c;答案在后面&#xff0…