当前位置: 首页 > news >正文

【iOS】多线程 锁问题总结

文章目录

  • 前言
    • 1. 你理解的多线程
      • 优点
      • 缺点
    • 2. atomic 和 nonatomic 的区别及其作用
    • 3. GCD的队列类型 - 三种队列类型
    • 4. GCD的死锁问题
    • 5. 多线程之间的区别和联系
    • 6. 进程和线程?
        • 进程间的通信方式
        • 线程间的通信方式
    • 6. iOS的线程安全手段如何保证

前言

iOS 锁和多线程的总结

1. 你理解的多线程

多线程是同时执行多个线程(子任务)的能力,用于提高程序性能和响应性。它允许在一个程序中并发地处理多个任务。

  • 并发:在一个时间段多个线程同时进行,计算机通过切换不同的线程实现多线程任务。

优点

  1. 大大提高了程序的运行速度。
  2. 使用线程可以把占据时间较长的任务放到后面去处理,从而提升用户的体验。

缺点

  1. 如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。
  2. 更多的线程需要更多的内存空间。

2. atomic 和 nonatomic 的区别及其作用

  1. atomic原子操作:加锁,保证setter和getter存取方法的线程安全(仅仅对setter和getter方法加锁)。因为线程加锁,别的线程访问当前属性的时候会先执行完属性当前的操作。
  • 对同一对象的set和get的操作是顺序执行的。
  • 速度不快,因为要保证操作整体完成。
  • 线程安全,需要消耗大量系统资源为属性加锁。
  • 使用atomic并不能保证绝对的线程安全,因为atomic仅仅是对系统生成的的settergetter方法加锁, 对于绝对保证线程安全的操作,需要使用更高级的方式处理,NSSpinLock, @syncronized 锁保证线程安全。
  1. nanatomic非原子操作,不加锁,线程执行快,但是多个线程访问同一个属性可能产生crash。
  • 不是默认的
  • 速度更快,如果有两个线程访问同一个属性可能造成crash。
  • 非线程安全
  1. atomic与nonatom的主要区别就是系统自动生成的getter/setter方法不一样
  • atomic系统自动生成的getter/setter方法会进行加锁操作。
  • nonatomic系统自动生成的getter/setter方法不会进行加锁操作。

⚠️:atomic修饰的属性,系统生成的 getter/setter 会保证 getset 操作的完整性,不受其他线程影响。比如,线程 A 的 getter 方法运行到一半,线程 B 调用了 setter:那么线程 A 的 getter 还是能得到一个完好无损的对象。

3. GCD的队列类型 - 三种队列类型

  1. The main queue(主线程串行队列)与主线程功能相同,提交到main queue的任务会在主线程中执行。
dispatch_get_main_queue() 来获取
  1. Global queue(全局并发队列) 全局并发队列由整个进程共享,有 高 中(默认是中) 低和后台四个优先级别。
dispatch_get_global_queue() 可以设置优先级
  1. Custom queue(自定义队列) 可以串行,也可以并发。
dispatch_queue_create()

4. GCD的死锁问题

概念:所谓死锁,通常是两个线程A和B都卡住了,A在等B,B在等A,互相等待到值死锁。

  1. .主线程串行队列同步执行任务,在主线程运行时,会产生死锁
NSLog(@"1"); // 任务1
dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@"2"); // 任务2
});
NSLog(@"3"); // 任务3

分析:

  • dispatch_sync表示是一个同步线程;
  • dispatch_get_main_queue表示运行在主线程中的主队列;
  • 任务2是同步线程的任务。
  • 任务3需要等待任务2结束之后再执行.

为什么造成死锁?

  1. 首先执行任务1,这是肯定没问题的,只是接下来,程序遇到了同步线程,那么它会进入等待,等待任务2执行完,然后执行任务3。但这是主队列,是一个特殊的串行队列,有任务来,当然会将任务加到队尾,然后遵循FIFO原则执行任务。那么,现在任务2就会被加到最后,任务3排在了任务2前面

任务3要等任务2执行完才能执行,任务2又排在任务3后面,意味着任务2要在任务3执行完才能执行,所以他们进入了互相等待的局面。【既然这样,那干脆就卡在这里吧】这就是死锁。

请添加图片描述

  1. 同步异步互相嵌套
// 同步 + 异步 互相嵌套产生死锁
- (void)sync_async {dispatch_queue_t queue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL);NSLog(@"1"); // 任务1dispatch_async(queue, ^{NSLog(@"2"); // 任务2dispatch_sync(queue, ^{NSLog(@"3"); // 任务3});NSLog(@"4"); // 任务4});NSLog(@"5"); // 任务5
}

请添加图片描述
分析:首先通过自定义队列创建了dispatch_queue_create函数创建了一个DISPATCH_QUEUE_SERIAL的串行队列。

  1. 执行任务1.
  2. 遇到异步线程,将【任务2、同步线程、任务4】加入串行队列中。因为是异步线程,所以在主线程中的任务5不必等待异步线程中的所有任务完成;
  3. 因为任务5不必等待,所以2和5的输出顺序不能确定;
  4. 任务2执行完以后,遇到同步线程,这时,将任务3加入串行队列;
  5. 又因为任务4比任务3早加入串行队列,所以,任务3要等待任务4完成以后,才能执行。但是任务3所在的同步线程会阻塞,所以任务4必须等任务3执行完以后再执行。这就又陷入了无限的等待中,造成死锁。
    在这里插入图片描述
    主线程无限循环
- (void)async_loop {dispatch_async(dispatch_get_global_queue(0, 0), ^{NSLog(@"1"); // 任务1dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@"2"); // 任务2});NSLog(@"3"); // 任务3});NSLog(@"4"); // 任务4while (1) {}NSLog(@"5"); // 任务5// a打印 4 1 / 1 4 顺序不定   
}

打印结果: 4 1 / 1 4 顺序不定

分析:

  • 先来看看都有哪些任务加入了Main Queue:【异步线程、任务4、死循环、任务5】。
  • 在加入到Global Queue异步线程中的任务有:【任务1、同步线程、任务3】。
  • 第一个就是异步线程,任务4不用等待,所以结果任务1和任务4顺序不一定。
  • 任务4完成后,程序进入死循环,Main Queue阻塞。但是加入到Global Queue的异步线程不受影响,继续执行任务1后面的同步线程。
  • 同步线程中,将任务2加入到了主线程,并且,任务3等待任务2完成以后才能执行。这时的主线程,已经被死循环阻塞了。所以任务2无法执行,当然任务3也无法执行,在死循环后的任务5也不会执行。

最终,只能得到1和4顺序不定的结果。

5. 多线程之间的区别和联系

在这里插入图片描述

GCD和NSOperation

  • GCD的执行效率更高,执行的是由Block构成的任务,是一个轻量级的数据结构,写起来更加方便
  • GCD只支持FIFO队列,NSOperationQueue可以通过设置最大并发数、设置优先级、添加依赖关系来调整执行顺序
  • NSOperation可以跨越队列设置依赖关系,GCD仅仅能通过栅栏等方法才能控制执行顺序
  • NSOperation更加面向对象,支持KVO,也可以通过继承等关系添加子类。
  • 所以如果我们需要考虑异步操作之间的顺序行、依赖关系,比如多线程并发下载等等,就使用NSOperation

GCD 与 NSThread 的区别

  • NSThread 通过 @selector 指定要执行的方法,代码分散, 依靠的是NSObject的分类实现的线程之间的通讯,如果要开线程必须创建多个线程对象。经常只用的是[NSTread current] 查看当前的线程。
  • NSThread是一个控制线程执行的对象,它不如NSOperation抽象,通过它我们可以方便的得到一个线程,并控制它。但NSThread的线程之间的并发控制,是需要我们自己来控制的,可以通过NSCondition实现。
  • GCD 通过 block 指定要执行的代码,代码集中, 所有的代码写在一起的,让代码更加简单,易于阅读和维护,不需要管理线程的创建/销毁/复用的过程!程序员不用关心线程的生命周期

6. 进程和线程?

参考:进程和线程的概念、区别及进程线程间通信
1. 基本概念:

  • 进程是对运行时程序的封装,是系统进行资源调度和分配的基本单位,实现了操作系统的并发。
  • 线程是进程的子任务,是CPU调度和分配的基本单位,用于保障程序执行的实时性,实现进程内部的并发。线程是操作系统可以识别的最小执行和调度单位。每个线程都肚子占用一个虚拟处理器等,每个线程完成不同的任务,但是共享同一地址空间(也就是同样的动态内存,映射文件,目标代码等等),打开的文件队列和其他内核资源。

2. 区别:

  • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。
  • 进程是资源分配的最小单位,线程是CPU调度的最小单位
  • 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。(资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。)
  • 进程间不会相互影响 ;线程:一个线程挂掉将导致整个进程挂掉

3. 通信方式

进程间的通信方式

  1. 进程间通信主要包括管道、系统IPC(包括消息队列、信号量、信号、共享内存等)、以及套接字socket。
  2. 信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器,可以用来控制多个进程对共享资源的访问。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。

线程间的通信方式

  1. 临界区:通过多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问;
  2. 互斥量Synchronized/Lock采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问
  3. 信号量Semphare:为控制具有有限数量的用户资源而设计的,它允许多个线程在同一时刻去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目。
  4. 事件(信号),Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作进程间通信的方式:

6. iOS的线程安全手段如何保证

参考:iOS中有哪些技术可以保证线程安全?
问:1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件。当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。此时,我们需要用线程锁来解决。

线程数据安全的方法:

  1. natomic原子操作:使用atomic多线程原子性控制,atomic的原理给setter加上锁,getter不会加锁。
  2. 使用GCD实现atomic操作:给某字段的setter方法和getter方法加上同步队列;
- (void)setCount:(NSInteger)newcount
{dispatch_sync(_synQueue, ^{count = newcount;});
}
- (NSInteger)count
{__block NSInteger localCount;dispatch_sync(_synQueue, ^{localCount = count;});return localCount;
}
  • 互斥锁能够有效的防止因多线程抢夺资源造成的数据安全问题,但是需要消耗大量的CPU资源。
  1. 互斥锁: 使用互斥锁可以确保同一时间只有一个线程访问共享资源。例如@synchronized创建互斥锁
@synchronized (self) {// 访问共享资源的代码
}
  1. 自旋锁:自旋锁(Spin Lock):自旋锁一种忙等待的锁,它会不断地尝试获取锁,直到成功为止。在Objective-C中,可以使用os_unfair_lock来创建自旋锁。
  2. 信号量(Semaphore):信量是一种数器,用于控制同时访问某个资源的线程数量。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 访问共享资源的代码
dispatch_semaphore_signal(semaphore);
  1. 串行队列:串行队列(Serial Queue):使用串行队列可以确保任务按顺序执行,从而避多个线程同时访问共享资源。可以使用GCD(Grand Central Dispatch)来创建串行队列。
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{// 访问共享资源的代码
});

相关文章:

【iOS】多线程 锁问题总结

文章目录 前言1. 你理解的多线程优点缺点 2. atomic 和 nonatomic 的区别及其作用3. GCD的队列类型 - 三种队列类型4. GCD的死锁问题5. 多线程之间的区别和联系6. 进程和线程?进程间的通信方式线程间的通信方式 6. iOS的线程安全手段如何保证 前言 iOS 锁和多线程的…...

Pytorch深度学习-----神经网络之池化层用法详解及其最大池化的使用

系列文章目录 PyTorch深度学习——Anaconda和PyTorch安装 Pytorch深度学习-----数据模块Dataset类 Pytorch深度学习------TensorBoard的使用 Pytorch深度学习------Torchvision中Transforms的使用(ToTensor,Normalize,Resize ,Co…...

Docker啥是数据持久化?

文章目录 数据持久化数据卷相关命令创建读写数据卷创建只读数据卷数据卷共享数据卷容器实现数据卷共享nginx实现数据卷共享nfs总结 Dockerfile持久化Dockerfile方式docker run总结 数据持久化 ​ 在容器层的 UnionFS(联合文件系统)中对文件/目录的任何修…...

CGAL 线段简化算法(2D)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 线段简化是指:在减少一组折线中顶点数量的同时,尽可能保持整体形状的过程。CGAL中提供了一种迭代算法:通过从一条折线上移除顶点 q q q,迭代地将边 ( p , q...

在CentOS 7上挂载硬盘到系统的步骤及操作

目录 1:查询未挂载硬盘2:创建挂载目录3:检查磁盘是否被分区4:格式化硬盘5:挂载目录6:检查挂载状态7:设置开机自动挂载总结: 本文介绍了在CentOS 7上挂载硬盘到系统的详细步骤。通过确…...

螺旋矩阵(JS)

螺旋矩阵 题目 给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1: 输入:n 3 输出:[[1,2,3],[8,9,4],[7,6,5]]示例 2: 输入&#xff…...

C#常用数学插值法

目录 1、分段线性插值 2、三次样条插值 3、拉格朗日插值 (1)一元全区间不等距插值 (2)一元全区间等距插值 4、埃尔米特插值 (1)埃尔米特不等距插值 (2)埃尔米特等距插值 1、…...

ELK日志管理平台架构和使用说明

一、部署架构 二、服务注册 2.1 日志解析服务 服务名:日志解析服务(Logstash) 服务默认端口:9600 2.2 日志查询服务 服务名:日志查询服务(Kibana) 服务默认端口:5601 三、对接…...

抖音短视频seo矩阵系统源码开发部署技术分享

抖音短视频的SEO矩阵系统是一个非常重要的部分,它可以帮助视频更好地被搜索引擎识别和推荐。以下是一些关于开发和部署抖音短视频SEO矩阵系统的技术分享: 一、 抖音短视频SEO矩阵系统的技术分享: 关键词研究:在开发抖音短视频SEO矩…...

docker 部署一个单节点的rocketmq

拉取镜像 sudo docker pull rocketmqinc/rocketmq创建数据挂载目录 mkdir -p /docker/rocketmq/data/namesrv/logs mkdir -p /docker/rocketmq/data/namesrv/store mkdir -p /docker/rocketmq/data/broker/logs mkdir -p /docker/rocketmq/data/broker/store /docker/…...

MySQL优化

目录 一. 优化 SQL 查询语句 1.1. 分析慢查询日志 1.2. 优化 SQL 查询语句的性能 1.2.1 优化查询中的索引 1.2.2 减少表的连接(join) 1.2.3 优化查询语句中的过滤条件 1.2.4 避免使用SELECT * 1.2.5 优化存储过程和函数 1.2.6 使用缓存 二. 优化表结构…...

【C++】总结9

文章目录 C从源代码到可执行程序经过什么步骤静态链接和动态链接类的对象存储空间C的内存分区内存池在成员函数中调用delete this会出现什么问题?如果在类的析构函数中调用delete this,会发生什么? C从源代码到可执行程序经过什么步骤 预处理…...

C++报错 XX does not name a type;field `XX’ has incomplete type解决方案

C报错 XX does not name a type;field XX’ has incomplete type解决方案 两个C编译错误及解决办法–does not name a type和field XX’ has incomplete type 编译错误一:XX does not name a type 编译错误二:field XX’ has incomplete t…...

28.利用fminsearch、fminunc 求解最大利润问题(matlab程序)

1.简述 1.无约束(无条件)的最优化 fminunc函数 : - 可用于任意函数求最小值 - 统一求最小值问题 - 如求最大值问题: >对函数取相反数而变成求最小值问题,最后把函数值取反即为函数的最大值。 使用格式如下 1.必须预先把函数存…...

图像 检测 - FCOS: Fully Convolutional One-Stage Object Detection (ICCV 2019)

FCOS: Fully Convolutional One-Stage Object Detection - 全卷积一阶段目标检测(ICCV 2019) 摘要1. 引言2. 相关工作3. 我们的方法3.1 全卷积一阶目标检测器3.2 FCOS的FPN多级预测3.3 FCOS中心度 4. 实验4.1 消融研究4.1.1 FPN多级预测4.1.2 有无中心度…...

C# NDArray System.IO.FileLoadException报错原因分析

C# NDArray System.IO.FileLoadException 报错原因分析: 1.NuGet程序包版本有冲突 2.统一项目版本 1.打开解决方案NuGet程序包设置 2.查看是否有版本冲突 3.统一版本冲突...

快速响应,上门维修小程序让您享受无忧生活

随着科技的不断发展和智能手机的普及,上门维修小程序成为了现代人生活中越来越重要的一部分。上门维修小程序通过将维修服务与互联网相结合,为用户提供了更加便捷、高效的维修服务体验。下面将介绍上门维修小程序开发的优势。   提供便捷的预约方式&am…...

05、性能分析思路?

工具操作:包括压力工具、监控工具、剖析工具、调试工具。数值理解:包括上面工具中所有输出的数据。趋势分析、相关性分析、证据链分析:就是理解了工具产生的数值之后,还要把它们的逻辑关系想明白。这才是性能测试分析中最重要的一…...

【编程语言 · C语言 · calloc和realloc】

【编程语言 C语言 calloc和realloc】https://mp.weixin.qq.com/s?__bizMzg4NTE5MDAzOA&mid2247491544&idx1&sn72d8f9931cfa7ce7441a3248475ab619&chksmcfade321f8da6a374a5935bb46441a03a007c0589db6b8afa8c1991854d632a3201553e37b0b&payreadticketHGy…...

机器学习分布式框架ray运行pytorch实例

Ray是一个用于分布式计算的开源框架,它可以有效地实现并行化和分布式训练。下面是使用Ray来实现PyTorch的训练的概括性描述: 安装Ray:首先,需要在计算机上安装Ray。你可以通过pip或conda来安装Ray库。 准备数据:在使用…...

TypeScript 【type】关键字的进阶使用方式

导语: 在前面章节中,我们了解到 TS 中 type 这个关键字,常常被用作于,定义 类型别名,用来简化或复用复杂联合类型的时候使用。同时也了解到 为对象定义约束接口类型 的时候所使用的是 Interfaces。 其实对于前面&#…...

策略路由实现多ISP接入Internet

组网需求&#xff1a; 企业分别从ISP1和ISP2租用了一条链路 PC3用户上网访问Server1时走ISP1PC4用户上网访问Server1时走ISP2 拓扑图 一、ISP1 运营商 R1路由器 <Huawei>sys [Huawei]sys R1 [R1]un in en[R1]int g0/0/0 [R1-GigabitEthernet0/0/0]ip addr 2.2.2.2 2…...

Socket本质、实战演示两个进程建立TCP连接通信的过程

文章目录 Socket是什么引入面试题, 使你更深刻的理解四元组 Socket网络通信大体流程实战演示TCP连接建立过程需要用到的linux 查看网络的一些命令测试的程序一些准备工作启动服务端, 并没有调用accept启动客户端开启服务accept Socket是什么 通俗来说,Socket是套接字,是一种编…...

java学习路程之篇四、进阶知识、石头迷阵游戏、绘制界面、打乱石头方块、移动业务、游戏判定胜利、统计步数、重新游戏

文章目录 1、绘制界面2、打乱石头方块3、移动业务4、游戏判定胜利5、统计步数6、重新游戏7、完整代码 1、绘制界面 2、打乱石头方块 3、移动业务 4、游戏判定胜利 5、统计步数 6、重新游戏 7、完整代码 java之石头迷阵单击游戏、继承、接口、窗体、事件、组件、按钮、图片...

Git全栈体系(三)

第六章 GitHub 操作 一、创建远程仓库 二、远程仓库操作 命令名称作用git remote -v查看当前所有远程地址别名git remote add 别名 远程地址起别名git push 别名 分支推送本地分支上的内容到远程仓库git clone 远程地址将远程仓库的内容克隆到本地git pull 远程库地址别名 远…...

JMeter发送get请求并分析返回结果

在实际工作的过程中&#xff0c;我们通常需要模拟接口&#xff0c;来进行接口测试&#xff0c;我们可以通过JMeter、postman等多种工具来进行接口测试&#xff0c;但是工具的如何使用对于我们来说并不是最重要的部分&#xff0c;最重要的是设计接口测试用例的思路与分析结果的能…...

HTML笔记(1)

介绍 浏览器中内置了HTML的解析引擎&#xff0c;通过解析标记语言来展现网页&#xff1b;HTML标签都是预定义好的&#xff1b;Java工程师&#xff1a;后台代码的编写&#xff0c;和数据库打交道&#xff0c;把数据给网页前端的工程师&#xff1b;网页前端工程师&#xff1a;写H…...

重新审视MHA与Transformer

本文将基于PyTorch源码重新审视MultiheadAttention与Transformer。事实上&#xff0c;早在一年前博主就已经分别介绍了两者&#xff1a;各种注意力机制的PyTorch实现、从零开始手写一个Transformer&#xff0c;但当时的实现大部分是基于d2l教程的&#xff0c;这次将基于PyTorch…...

Docker 全栈体系(七)

Docker 体系&#xff08;高级篇&#xff09; 五、Docker-compose容器编排 1. 是什么 Compose 是 Docker 公司推出的一个工具软件&#xff0c;可以管理多个 Docker 容器组成一个应用。你需要定义一个 YAML 格式的配置文件docker-compose.yml&#xff0c;写好多个容器之间的调…...

【编程范式】聊聊什么是数据类型和范式的本质

什么是编程范式 范式其实就是做事的方式&#xff0c;编程范式可以理解为如何编程&#xff0c;按照什么样的模式或者风格进行编程。 编程范式包含哪些 泛型编程函数式编程面向对象编程编程本质和逻辑编程 虽然有不同的编程范式&#xff0c;但是对于目的来说都是为了解决同一…...