【iOS】多线程

文章目录

  • 前言
  • 一、多线程的选择方案
  • 二、GCD和NSOperation的比较
  • 二、多线程相关概念
    • 任务
    • 队列
  • 三、死锁情况
    • 主队列加同步任务
  • 四、任务队列组合
    • 主队列+异步
    • 并发队列+异步


前言

这两天将iOS的多线程的使用都看了一遍,iOS的多线程方案有许多,本篇博客主要总结一下不同方案之间的区别以及面试一般会问到的问题

一、多线程的选择方案

技术方案简介语言线程生命周期使用频率
pthread一套通用的多线程API,适用于Unix/Linux/Windows等系统,跨平台/可移植,使用难度大C程序员管理几乎不用
NSThreadNSThread是苹果官方提供的,使用起来更加面向对象,简单易用,可以直接操作线程对象OC程序员管理几乎不用
GCD旨在替代NSThread等线程技术充分利用设备的多核C自动管理经常使用
NSOperation基于GCD的更高一层封装OC自动管理经常使用

二、GCD和NSOperation的比较

关系:
GCD面向底层C语言的API
NSOperation使用GCD进行封装的

对比

  • GCD是苹果官方推荐的多线程方案,使用起来比较方便,更加轻量级
  • GCD只支持FIFO的队列,NSOperation可以设置最大并发数控制串行并行以及通过添加依赖关系来调整执行顺序,这些都是GCD做不到的
  • NSOpration甚至可以跨队列设置依赖关系,但是GCD只能通过设置串行队列,或者在队列内添加barrier任务才能控制执行顺序,较为复杂
  • NSOperation支持KVO(面向对象)可以检测operation是否正在执行、是否结束、是否取消
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.operationQueue = [[NSOperationQueue alloc] init];
    
    [self.operationQueue addObserver:self
                          forKeyPath:@"operations"
                             options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
                             context:NULL];
}

二、多线程相关概念

任务

就是执行的操作,比如GCD中的Block。
执行任务有两种方式:同步执行与异步执行

同步执行(sync):

  • 同步添加任务到指定的队列中,在添加的任务结束之前线程会一直等待,无法执行其他操作
  • 只能在当前线程中执行任务,不具备开启新线程的能力。

异步执行(async):

  • 异步添加任务到指定的队列中,它不会做任何等待,添加后立即返回不会阻塞线程、可以继续执行任务.
  • 可以在新的线程中执行任务,具备开启新线程的能力。

这样子来看可能比较抽象,我们用代码层面来理解就是:

进入dispatch_sync函数后程序不会立刻返回,因此会阻塞线程
而进入dispatch_async函数后程序会立刻返回,不会妨碍进行下一步操作

队列

这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)的原则。GCD 中有两种队列串行队列和并发队列。两者都符合 FIFO(先进先出)的原则。两者的主要区别是:执行顺序不同,以及开启线程数不同。

  • 串行队列(Serial Dispatch Queue):每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务
  • 并发队列(Concurrent Dispatch Queue):可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务

三、死锁情况

主队列加同步任务

- (void)syncTaskWithMain {
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_sync(mainQueue, ^{
        NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
        NSLog(@"任务2");
    });
}

// 在主线程中调用 syncTaskWithMain
- (void)viewDidLoad {
    [super viewDidLoad];
    [self syncTaskWithMain];
}

在上述代码中,我们在主线程的viewDidLoad方法中调用了asyncTaskWithMain函数,而在asyncTaskWithMain函数中,我们使用dispatch_sync向主队列提交了一个同步任务。
这种情况会导致死锁,原因如下:

  1. [self syncTaskWithMain]被调用时,主线程进入了asyncTaskWithMain函数。
  2. asyncTaskWithMain函数中,dispatch_sync函数会等待主队列空闲,然后在主队列中同步执行提交的任务。
  3. 但是,由于主线程正在执行syncTaskWithMain函数,所以主队列被占用,无法空闲。
  4. 因此,dispatch_sync函数会一直等待主队列空闲,而主线程也会一直被阻塞在syncTaskWithMain函数中,从而导致死锁。

为了避免死锁,我们应该在syncTaskWithMain函数中使用dispatch_async函数向主队列提交异步任务,或者在其他串行队列中使用dispatch_sync函数提交同步任务。

也就是二者相互等待造成死锁

四、任务队列组合

在这里插入图片描述
也就是只有当异步才会开辟新线程

主队列+异步

这里需要注意的一点是主队列+异步不会开辟新线程,这是由主队列的特性决定的

主队列+异步时既不会创建新的线程,也不会阻塞线程,我们来分析一下原因

主队列(Main Dispatch Queue)
主队列是一个特殊的串行队列,它在应用的主线程上执行任务。由于它是串行的,它一次只能执行一个任务。这意味着如果你在主队列上使用 dispatch_async 来提交一个任务,这个任务会被放到队列的末尾等待执行。

为什么不会创建新的线程
使用 dispatch_async 将任务提交到主队列时,这些任务不会创建新线程,因为:

  • 主队列特性:
    主队列绑定于主线程,所有在主队列上提交的任务都必须在主线程上执行。这是由于主线程通常处理所有的 UI 更新和用户交互,所以系统保证主队列的任务在主线程上顺序执行。
  • 任务调度:
    dispatch_async 函数只是将任务异步地放入队列中,它本身不关心任务的执行在哪个线程进行,任务的执行线程完全取决于队列的性质。由于主队列特性,即使使用 dispatch_async,任务仍然会在主线程上执行

为什么不会导致阻塞
使用 dispatch_async 提交任务到主队列时,不会阻塞主线程,原因包括:

  • 异步执行:
    dispatch_async 是异步执行的,这意味着它会立即返回,不会等待任务执行完成。因此,即使任务最终在主线程上执行,dispatch_async 本身也不会阻塞主线程。
  • 执行时机:
    由于主队列是串行队列,提交到主队列的异步任务会在先前的任务完成后按顺序执行。如果主线程当前正在处理其他任务(如用户输入、动画等),那么主队列上的任务会等待直到主线程空闲。

也就是当主线程的所有的原先的任务都执行完后才回去执行添加到主队列中的异步任务

-(void)asyncTaskWithMain{
    NSLog(@"currentThread---%@",[NSThread currentThread]);
    dispatch_queue_t q = dispatch_get_main_queue();
    dispatch_async(q, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
        NSLog(@"任务1");
    });
    NSLog(@"4");
}

// viewC
    [self asyncTaskWithMain];
    NSLog(@"等待");
    NSLog(@"等待");
    NSLog(@"等待");
    NSLog(@"等待");

输出:
在这里插入图片描述

并发队列+异步

同时如果是全局并发队列+异步,那么不会等待主队列任务执行完毕,两个线程会同时执行任务

    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(globalQueue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
        NSLog(@"任务1");
    });

输出:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

打开深度学习的锁:(0)什么是神经网络?

PS:每每温故必而知新 什么是神经网络? 一、一个单神经元的神经网络二、多个单神经元的神经网络三、到底什么是机器学习?(重点)1:什么是机器学习的训练?2:什么是模型?权重…

python数据分析所需要的语法基础

Python语言基础——语法基础 前言语法基础变量标识符数据类型输入与输出代码缩进与注释 总结 前言 对于学过C语言的人来说,python其实很简单。学过一种语言,学习另一种语言,很显然的能感觉到,语言大体上都是相通的。当然&#xf…

【docker】常用的把springboot打包为docker镜像的maven插件

Spring Boot Maven Plugin: Spring Boot 自带的 Maven 插件 (spring-boot-maven-plugin) 支持直接生成 Docker 镜像。通过配置,可以在 Maven 构建过程中自动构建 Docker 镜像,而无需单独编写 Dockerfile。这种方法简化了将应用打包为 Docker 镜像的过程。…

武汉理工大学python123实验——流程控制结构

1.百分制成绩转换五分制#1707 n int(input())if n>90:print(A) elif n>80:print(B) elif n>70:print(C) elif n>60:print(D) else:print(E) 2.角古猜想#73963 n eval(input()) if n<0:print(ERROR) elif . in str(n):print(ERROR) else:print(n,end" …

Qt扫盲-Qt D-Bus概述

Qt D-Bus概述 一、概述二、总线三、相关概念1. 消息2. 服务名称3. 对象的路径4. 接口5. 备忘单 四、调试五、使用Qt D-Bus 适配器1. 在 D-Bus 适配器中声明槽函数1. 异步槽2. 只输入槽3. 输入输出槽4. 自动回复5. 延迟回复 一、概述 D-Bus是一种进程间通信(IPC)和远程过程调用…

Java面试题:多线程2

如何停止正在运行的线程 1,使用退出标志,使线程正常退出(run方法中循环对退出标志进行判断) 2,使用stop()方法强行终止(不推荐) 3,调用interrupt()方法中断线程 打断阻塞线程(sleep,wait,join),线程会抛出InterruptedException异常 打断正常的线程,可以根据打断状态来标记…

继承知识及扩展(C++)

1. 继承是什么&#xff1f; 继承是面向对象编程的三大特征之一&#xff0c;也是代码复用的手段之一。之前我们在很多的地方尝试函数的复用&#xff0c;而继承是为了类的复用提供了很好的方式。 &#xff08;1&#xff09;继承的代码怎么写 在一个类后面使用 &#xff1a;继承方…

知识图谱融入RAG模型:LinkedIn重塑智能客服新范式【附LeCun哈佛演讲PPT】

原文&#xff1a;Retrieval-Augmented Generation with Knowledge Graphs for Customer Service Question Answering 一、研究背景与问题 在客服领域,快速准确地匹配用户问题与历史工单,是提供优质回答的关键。传统的检索增强生成(Retrieval-Augmented Generation, RAG)方法虽…

IDEA-控制台日志过滤插件 - Grep Console

IDEA-控制台日志过滤插件 - Grep Console 当idea控制台日志较多时&#xff0c;为了方便查找关键字&#xff0c;使用Grep Console插件&#xff0c;指定控制台中关键字高亮显示 1.安装 2.使用 2.1 高亮显示 控制台中指定颜色高亮显示指定字符 效果: 重启项目后还是会高亮显示 取…

【软考高项】三十三、质量管理

一、管理基础 质量定义 国际标准&#xff1a;反映实体满足主体明确和隐含需求的能力的特性总和。 国家标准&#xff1a;一组固有特性满足要求的程度。固有特性是指在某事或某物中本来就有的&#xff0c;尤其是那种永久的可区分的特征。 ➢ 对产品来说&#xff0c;例如…

缓存菜品操作

一&#xff1a;问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大。 二&#xff1a;实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; 每个分…

k8s保持pod健康

存活探针 Kubemetes 可以通过存活探针 (liveness probe) 检查容器是否还在运行。可以为 pod 中的每个容器单独指定存活探针。如果探测失败&#xff0c;Kubemetes 将定期执行探针并重新启动容器。 Kubemetes 有以下三种探测容器的机制&#xff1a; HTTP GET 探针对容器的 IP 地…

Day61:单调栈 739. 每日温度 496.下一个更大元素 I

739. 每日温度 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替。 示例 1: 输…

发表博客之:gemm/threadblock/threadblock_swizzle.h 文件夹讲解,cutlass深入讲解

文章目录 [发表博客之&#xff1a;gemm/threadblock/threadblock_swizzle.h 文件夹讲解&#xff0c;cutlass深入讲解](https://cyj666.blog.csdn.net/article/details/138514145)先来看一下最简单的struct GemmIdentityThreadblockSwizzle结构体 发表博客之&#xff1a;gemm/th…

vue2 webpack-dev-server Unknown promise rejection reason

在vue.config.js中添加如下配置&#xff0c;重启项目即可 module.exports defineConfig({devServer: {client: {overlay: false,},} })参考

探索中位数快速排序算法:高效寻找数据集的中间值

在计算机科学领域&#xff0c;寻找数据集的中位数是一个常见而重要的问题。而快速排序算法作为一种高效的排序算法&#xff0c;可以被巧妙地利用来解决中位数查找的问题。本文将深入探讨中位数快速排序算法的原理、实现方法以及应用场景&#xff0c;带你领略这一寻找中间值的高…

vue 金额组件,输入提示单位:‘千’、‘万’、‘十万’...并用‘,’三个格式化

近期项目中遇到一个需求&#xff0c;金额输入框&#xff0c;输入过程中自动提示‘千’、‘万’、‘十万’、‘百万’......等单位提示&#xff0c;鼠标失去焦点后&#xff0c;并用‘,’三位隔开计数。 例如&#xff1a; 输入&#xff1a;12345.99 失去焦点&#xff1a;12,34…

Vue--》从零开始打造交互体验一流的电商平台(一)

今天开始使用 vue3 ts 搭建一个电商项目平台&#xff0c;因为文章会将项目的每处代码的书写都会讲解到&#xff0c;所以本项目会分成好几篇文章进行讲解&#xff0c;我会在最后一篇文章中会将项目代码开源到我的github上&#xff0c;大家可以自行去进行下载运行&#xff0c;希…

【Node.js工程师养成计划】之express中间件与接口规范

一、Express中间件的概念与基本应用 const express require(express)// 加一个注释&#xff0c;用以说明&#xff0c;本项目代码可以任意定制更改 const app express()const PORT process.env.PORT || 3000// // 挂载路由 // app.use(/api, router)// // 挂载统一处理服务端…

【倪亲斫经典水墨云纹仲尼式】倪诗韵亲斫古琴

【倪亲斫经典水墨云纹仲尼式】倪诗韵亲斫古琴 松透润&#xff0c;适合大曲文曲潇湘欸乃平沙&#xff0c;余韵悠长&#xff0c;手感极其舒适&#xff0c;久弹不疲。