ios一窥并发底层

限时加码!20+主流AI编程工具免费用 购周边加赠Coding Plan Lite,Claude Code、Cursor等即刻畅享,学习进阶更高效! 阅读详情

逻辑控制流

在我们系统中通常是会有其它程序在运行,进程是可以告诉每一个程序它是独自在使用处理器。这个时候如果有调试器单步去执行程序,就会出现一系列的程序计数器( PC ) 值,这些值唯一的对应于包含在程序的可执行目标文件的指令。这个所谓的 PC 值叫做 逻辑控制流

一句话简单的介绍什么是并发:

  • 如果逻辑控制流在时间上重叠就是并发 (concurrent)
    e.g:

往宏观上讲:在计算机系统中硬件异常处理程序, 我们进行 Command + C的时候
往微观上讲:I/O 多路复用,应用程序在一个进程的上下文中显示地调度它们的逻辑流。逻辑流被模型化为状态机,数据到达文件描述符后,主程序显式地从一个状态转换到另一个状态。
…. 如果在底层上面扣太多,就不是 iOS 方面的内容了。

我们知道对应用层的开发都是通过对底层的一个API 的封装,这里也会简单介绍一下底层方面的理论。

如果要了解并发编程就免不了进程,线程这些字眼。

进程

我相信从事 IT 行业的开发人员,对于它是不陌生的,千篇一律的话我就不多说了。下面就简单聊点不常知道的。

首先进程有独立的虚拟地址空间,如果想要和其他流进行通信,就是进程与进程之间进行通信,控制流必须使用某种显示的进程间通信机制 (IPC)

线程

线程是运行在一个单一进程上下文的逻辑流,由内核进行调度。

Obj中国 上我看到有用 pthread 进行示例证明。 我这里会适当的补充一点。

Posix 线程(Pthread) 是在 C 程序中处理线程的一个标准接口,在所有的 Linux 上都适用。 那么 Objective- C 对它的依赖就可想而知了。

Pthread 定义了大约 60多个 个函数,它们分别用来进行创建,杀死,和回收线程.对线程安全地共享数据,通知对等线程系统状态的变化等等。

直接适用 Pthread 的函数是非常繁琐的,那么到了 OC 这里就免不了对它进行了二次封装, 就这样来到了 Cocoa 那么到了 Swift 里面也基本是换汤不换药的拿来即用。

现在正式来到多线程的世界

先说点不厌其烦的废话知识: 锁

在 Objective-C 中常用的加锁方式有用 @synchronized 来修饰变量,以此来保证变量在作用范围内不会被其他线程改变。

那在 Swift 中是用 objc_sync_enter 与 objc_sync_exit 配合来使用加锁

上面提到锁相关的一些信息,那么它存在的目的无非就是一个:就是在多线程共享相同的程序变量

那么它在底层的原理是什么?正是这里要讲的。

问题
1.多线程存在的时候其基础的内存模型是什么?
2.变量如何映射到内存里面去?
3.引用这些变量的线程有多少?

每个线程都有它自己独立的线程上下文,包括线程的 ID, 栈 , 栈指针 ,程序计数器, 条件码, 通用目的寄存器

每个线程和其他线程一起共享进程的上下文的剩余部分。这包括整个用户虚拟地址空间,它是由只读文本,读/写数据, 栈以及所有的共享库代码和数据区域组成。

任何线程都可以访问共享虚拟内存的任意位置, 如果众多线程中的某一个线程修改了一个内存位置,那其他的线程都能在它读到这个位置时发现这个变化。

虚拟内存对相关变量的一些操作

1、全局变量:
虚拟内存的读/写区域只包含每个全局变量的一个实例,任何线程都可以调用

2、本地变量:
每个线程的栈都包含它自己的所有本地自动变量

3、本地的静态变量:本地带 Static 属性, 虚拟内存的读/写区域只包含程序中声明的每个本地静态变量的一个实例

信号量

  • 计数器 (引用计数)
  • 同步错误 synchronization error
  • 进度图 progress graph
计数器 (引用计数) 与 同步错误 (synchronization error)

在多线程访问同一个全局变量的时候,我们查看对计数相关的汇编代码,过程大致如下:
1、加载全局变量 cnt 到累加寄存器 %rdx (当前线程的寄存器 %rdx 的值)
2、增加 %rdx 的指令
3、将%rdx的更新值存回到共享变量 cnt 的指令

当然在iOS当中不会直接这样计数,因为这样会存在一个很大的问题:

当两个线程同时对一个计数器的值进行读取,并加1,再将结果写到内存中去,这个时候计数器就会出现问题。因为计数器加了两次而写到内存中确是相当于只加了一次的那个值

引用 Obj中国 上面一个例子:

线程 A 和 B 都从内存中读取出了计数器的值,假设为 1 ,然后线程A将计数器的值加1,并将结果 2 写回到内存中。同时,线程B也将计数器的值加 1 ,并将结果 2 写回到内存中。实际上,此时计数器的值已经被破坏掉了,因为计数器的值 1 被加 1 了两次,而它的值却是 2。

在iOS 开发的应用层上面来看就是加锁等等一系列的操作。在真正核心底层方面好多文章是没有具体去讲的,您可以综合性的看看其他的文章,推荐 Obj中国 上面关于多线程系统的讲法。好了,我们继续

进度图 progress graph

将 n 个并发线程的执行抽象为一条 n 维 笛卡尔空间 中的轨迹线。
这里写图片描述

每条轴的 k 对应线程 k 的进度。每个点代表线程 k已经完成了指令 I_k的状态。

我们上面讲的:

在多线程访问同一个全局变量的时候,我们查看对计数相关的汇编代码,过程大致分为3步

我们这里设定在 A 线程的时候步骤为:
这里写图片描述

同理设定在 B 线程的时候步骤为:

这里写图片描述

这个时候我们来看下图

这里写图片描述

我们看到图中有一个点 (A1,B3)。

这个点的意思就是:当线程 A 完了第 A1 状态的同时,线程 B 完成了 B3 状态。

使用进度图的目的就是讲指令执行模型转化为从一种状态到另一种状态的转换。

这样就可以把程序的执行历史转换为状态空间中的一条轨迹线。

对于线程不管是 A 或者 B 也好,对全局变量的的操作(A1,A2,A3)步骤或者 (B1,B2,B3)步骤的过程中构成了一个临界区,这个临界区不应该和其他进程的临界区交替执行。我们确保每个线程在执行它的临界区中的指令时,拥有对共享变量 的 互斥 的访问( Mutually exclusive access). 通常这种现象称为互斥(Mutual exclusion).

这样在上图里面会出现这样的规则:相同指令不能再同一时刻完成,对角线的线是不存在的。
这里写图片描述

两个临界区的交集形成的状态空间区域称为不安全区(unsafe region)

这里写图片描述

安全轨迹线:不在不安全区的轨迹线
不安全轨迹线:雷区的轨迹线

任何安全轨迹线都将正确地更新共享计数器。为了保证任意的全局变量在并发线程的正确执行,我们就必须以某种方式同步线程,使他们总是有一条安全轨迹线。其思想原理的基本思想就是基于信号量

信号量: (semaphore) 一种特殊类型的变量。
信号量以s表示.是具有非负整数值的全局变量,只能有两种特殊的操作来处理,这两种操作称为 P 和 V:

P(s): 如果 s 是非零的,那么P 将 s 减1,并且立即返回。如果 S 为零,那么就挂起这个线程,直到 s 为零,而一个 V 操作会重启这个线程。在重启之后,P 操作将 s减1,并将控制返回给调用者。
V(s): V操作将 s 加1。如果有任何线程阻塞在 P 操作等待 s 变成非零,那么 V 操作会重启这些线程中的一个,然后该线程将 s 减1,完成它的 P 操作。
P 中的测试和减1操作是不可分割的,一旦预测信号量s 变为非零,就会将s减1,不能有中断操作,这个过程中不会有中断。 V 的加1 操作也是不可分割的。

没有中断的操作

这里写图片描述

PS:V 的定义中没有定义等待线程被重启动的顺序。唯一的要求是 V 必须只能重启一个正在等待的线程。因此,当有多个线程在等待同一个信号量时,就不能预测 V 操作要重启哪一个线程。

P和V 的定义确保了一个正在运行的程序绝不可能进入这一种状态,也就是一个正确初始化了的信号量有一个负值。这个属性称为 信号量不变性(semaphore invariant)

使用信号量来实现互斥

作用是:

将每个全局变量 与 一个信号量 s = 1 联系起来,然后用 P(s) 和 V(s) 操作将相应的临界区包围起来。这种方式成为 二元信号量 (binary semaphore),它的值要么是 0 要么是 1。以提供互斥为目的的二元型号量常常称为 互斥锁 (mutex).

那么在一个互斥锁上执行 P 操作称为对互斥锁加锁。执行 V操作称为对互斥锁解锁。对一个互斥锁加了锁但是还没有解锁的线程称为占用了这个互斥锁。 一个被用作一组可用资源的计数器的信号量被称为 计数信号量。

如上面的雷区图,在雷区内因为信号量的不确定性故: s < 0

以上看到的仍然是坑: 因为上面是单处理器的讲解

但是有一个是万用的:同步对共享变量的访问是必须的。

多线程中对相同资源的访问:

案例1:
在多媒体开发过程中对视频的帧编码,并实时播放。这个时候就会有一个缓存的东西存在,其存在的目的是为了减少视频流的抖动,引起的原因是帧的编码与解码时与数据相关的差异引起的。

案例2:
我们开发过程中对手机屏幕点击事件的产生后,该事件先进入缓存中,然后多线程根据优先级来从缓冲区里面取出该事件进行响应。这就能很好解释有时点击屏幕卡屏了一会儿才响应。

饥饿问题:
这个网上帖子泛滥: 传送门 Obj中国

多个线程并行处理分配给它们的区域处理方法:

主线程给其他开的线程一个整数理解为该线程的 ID。每个线程用它的ID来决定它应该计算序列的哪一部分。

并行程序的性能

运行时间是衡量程序性能的最终标准。相对衡量标准能够说明并行程序有多好地利用了潜在的并行性。

并行程序的加速比(speedup)通常定义为: Sp = T1/Tp

p 是处理器的核树,Tk 是在 K 个核上的运行时间。这个公式被称为:强扩展(strong scaling).

当T1是程序顺序执行版本的执行时间时,Sp称为 绝对加速比 (absolute speedup).
当T1是程序并行版本在一个核上的执行时间,Sp称为 相对加速比 (absolute speedup).
绝对加速比会比相对加速比更加难以测量,因为测量绝对加速比需要程序的两种不同的版本。对于复杂的并行代码,创造一个独立的顺序版本也不现实。

效率: Ep = Sp/p = T1/pTp

弱扩展:(weak scaling):在增加处理器数量的同时,增加问题的规模,这样随着处理器的数量的增加,每个处理器执行的工作量保存不变,在这样的情况下加速比和效率被表达为单位时间完成的工作量。

线程安全

首先被称为线程安全是当且仅当被多个多线程反复的调用,它才会一直产生正确的结果。如果一个函数设计的不是线程安全的,它就是线程不安全的。

线程不安全的函数定义:

1、对全局变量的保护
2、保存跨越多个调用的状态函数。如:随机数生产的函数
3、返回指向静态变量的指针的函数。
4、调用线程不安全函数的函数。

iOS底层原理系列01-iOS系统架构概览-从硬件到应用层 iOS系统架构是个精心设计的分层体系,从底层Darwin到用户交互的Cocoa Touch层,每层都有其明确的职责和功能。系统启动过程涉及多个阶段,从安全启动链到应用初始化,确保了系统的安全性和高效运行。iOS的进程和线程管理机制则提供了强大的并发处理能力,通过严格的资源管理和先进的调度算法,在保证用户体验的同时优化设备能耗。 阅读详情

相关推荐

iOS 多线程开发之OperationQueue()概念+两种Operation

原创blog,转载请注明出处 blog.csdn.net/hello_hwc 欢迎关注我的iOS SDK详解专栏,这里有很多基础的文章 http://blog.csdn.net/column/details/huangwenchen-ios-sdk.html前言:在iOS开发 中,多线程是个很重要的个方面。iOS的多线程使用可以分为几个方面。由底层到上层分别是 pthread NSThre

Leo的专栏 4714

并行算法的设计与分析

并行算法设计 任务并行 数据并行 与任务并行不同,前者是划分操作和计算任务,核心对于数据进行不同的运算;后者是划分数据,而核心对于数据进行相同的运算。 其他任务划分方法 搜索分解 将搜索树的每个子树划分成个任务,与数据分解的区别在于,前者的所有计算工作都是有用的,对于后者旦找到解,其他搜索工作也停止。 工作量可能大于也可能小于串行算法。 并行算法分析 性能评价标准 运行时间 T p T_pT p ​ 并行算法开始到最后个进程结束所需要的时间。 并行算法额外总开销 T o = p T p − T s T

计算机辅助工程 1166

爬虫逆向:详细讲述iOS底层原理及机制

iOS底层原理和机制涉及多个层次和组件,从 Darwin 内核到 Cocoa Touch 层,每个部分都发挥着重要作用。通过理解这些原理,开发者可以更好地优化应用性能、解决复杂问题,并深入掌握 iOS 系统的运行机制。

数据知道的博客 1万+

强扩展性和弱扩展性

弱扩展和强扩展都是高性能计算里的名词,也就是并行计算相关方面的。对于超级计算机而言,有成千上万的服务器节点,而如何充分的利用到这些服务器,我们需要设计合适的算法来利用好这些服务器,而强扩展性和弱扩展性是描述对应算法的指标。

weixin_52190646的博客 2910

iOS 并发编程底层()

点击上方“iOS开发”,选择“置顶公众号”关键时刻,第时间送达!逻辑控制流在我们系统中通常是会有其它程序在运行,进程是可以告诉每个程序它是独自在使用处理器。这个时候如果有调试器单步去执行程序,就会出现系列的程序计数器( PC ) 值,这些值唯的对应于包含在程序的可执行目标文件的指令。这个所谓的 PC 值叫做 逻辑控制流句话简单的介绍什么是并发:如果逻辑控制流在时间上重叠就是并发 (con

iOS_开发 399

Objc的底层并发API

http://www.cocoachina.com/appstore/case/2013/0531/6312.html 本文作者朱念洋:谨以此文献给与我同样有创业梦想的程序员们.   我相信像我样的程序员定大有人在,个人、身技术、腔热血,想要涉足手游领域,却不清楚会投入多少。   所以,我把自己的这次游戏开发过程,完整记下来,给与我同样的程序员以

呆萌院长 1636

iOS底层原理系列04-并发编程

iOS提供了多种并发编程和线程安全的机制,从底层的pthread到高级的GCD和NSOperation。性能需求:对于性能要求极高的场景,考虑os_unfair_lock或dispatch_semaphore;对于般场景,NSLock和串行队列足够。编程范式:如果偏好面向对象的API,选择NSOperation和NSLock系列;如果偏好函数式编程,选择GCD。任务特性:如果需要复杂的任务依赖和取消机制,选择NSOperation;如果是简单的并发任务,GCD更简洁。同步需求。

但行好事,莫问前程;不忘初心,方得始终! 1214

底层并发 API

底层并发 API 这篇文章里,我们将会讨论iOS和OS X 都可以使用的底层API. 出了dispatch_once,我们般不鼓励使用其中的任何种技术。我们想要揭示出表面之下深层次的些可利用的方面。这些底层的 API 提供了大量的灵活性,随之而来的是大量的复杂度和更多的责任。在我们的文章常见的后台实践中提到的高层的 API 和模式能够让你专注于手头的任务并且

Guide_Sun的博客 1099

iOS GCD中控制最大并发

、概述 在GCD中有两种队列,分别是串行队列和并发队列。在串行队列中,同时间只有个任务在执行,不能充分利用多核 CPU 的资源,效率较低。 并发队列可以分配多个线程,同时处理不同的任务;效率虽然提升了,但是多线程的并发是用时间片轮转方法实现的,线程创建、销毁、上下文切换等会消耗CPU 资源。 目前iPhone的处理器是多核(2个、4个),适当的并发可以提高效率,但是无节制地并发,如将大...

gcs的博客 2398

iOS 实现底层并发

目录 1、从前。。。  2、延后执行  3、队列      3.1、标记队列      3.2、优先级  4、孤立队列      4.1、资源保护      4.2、单资源的多读单写      4.3、锁竞争      4.4、全都使用异步分发      4.5、如何写出好的异步API  5、迭代执行  6、组 

FirstBloodFB的专栏 790

iOS--底层学习--GCD的简单认识

前段时间看了阵子源码,但遇到了大量的线程操作,导致看起来迷迷糊糊的,而且看源码的方式也有点不对,所以现在打算先学习部分iOS底层知识后,再回去重新过遍源码;这里先来了解下之前蓝书和小白书上的GCD;这里参考了这位大神的博客iOS多线程:『GCD』详尽总结iOS中的GCD是什么,简单介绍下:GCD,全称为Grand Central Dispatch,是苹果公司推出的种用于多核编程的技术,首次出现在Mac OS X 10.6 Snow Leopard和iOS 4中。

m0_74217598的博客 990

iOS底层探索之多线程(五)—GCD不同队列源码分析

回顾 在上篇博客已经介绍了各种队列和异步、同步函数的组合,GCD的队列和函数,对队列和任务的执行有了清晰的认识, 那么本篇博客将继续介绍GCD的队列和源码分析。 iOS底层探索之多线程()—进程和线程 iOS底层探索之多线程(二)—线程和锁 iOS底层探索之多线程(三)—初识GCD iOS底层探索之多线程(四)—GCD的队列 1. 主队列分析 查看主队列的api如下图: 主队列是个特殊的串行队列 主队列在调用main()函数之前自动创建的。 主队列在应用程序上下文中用于与主线程和main runl

俊而不逊的博客 1099

iOS】weak底层实现

不增加引用计数:weak指针指向对象时,对象的retainCount不会加1,不持有对象的生命周期。自动置空:当对象的引用计数变为0,执行dealloc释放后,所有指向该对象的weak指针会被Runtime自动置为nil,彻底杜绝野指针崩溃。Q:为什么weak会自动置nil?A:普通指针保存对象是存储对象的内存地址,而weak则是Runtime在SideTable中内部维护了张weak引用表,记录了所有指向该对象的weak指针地址。

2401_87841908的博客 388

iOS 并发:NSOperation 与调度队列入门(1)

并发就是柄值得你学习使用并熟练掌握的双刃剑。它能帮助你打造高效、迅捷、响应及时的应用。于此同时,旦误用,也会毫不留情地毁掉应用。因此,在开始编写并发代码之前,好好想想你为什么需要并发,你需要哪个 API 来解决问题?在 iOS 开发中,可用的 API 有很多。在本教程中,我们将探讨最常用的两个 API——NSOperation 以及调度队列。

wangpeng198688的专栏 3923

handler原子锁_OC的底层并发API

这篇文章里,我们将会讨论iOS和OSX都可以使用的底层API。除了dispatch_once,我们般不鼓励使用其中的任何种技术。但是我们想要揭示出表面之下深层次的些可利用的方面。这些底层的API提供了大量的灵活性,但是伴随着灵活性而来的却是程序复杂度的提升和我们对代码的更多责任。在我们的文章《commonbackgroundpractices》中提到的高层次的API和模式能够让你专注于手...

weixin_39782355的博客 312

ios并发会造成什么问题_iOS开发中设计并发任务技术与注意事项

Dispatch Queues简述Dispatch Queues是基于C语言的,执行自定义任务的技术,从字面意思理解其实就是执行任务的队列,使用GCD执行的任务都是放在这个队列中执行的,当然队列的数量可以有多个,类型也不止种。个Dispatch queue可以串行的执行任务,也可以并行的执行任务,但不管哪种执行任务的方式,都遵循先进先出的原则。串行队列次只能执行个任务,当前任务执行完后才能...

weixin_39717598的博客 87
上一篇: iOS高性能图片架构与设计
下一篇: iOS重签名防护和sysctl调试
Philm_iOS
博客等级 码龄8年 74粉丝 53原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值