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

iOS主要知识点梳理回顾-2-多线程

iOS的多线程主要有三种方式,NSThread、GCD(Grand Central Dispatch)NSOperationQueue

        开始,在iOS2发布的时候,苹果同步推出了NSthread和NSOperation。其中NSthread比较简单,仅提供了创建队列、开始、取消、关闭等简单动作。而NSOperation就更高级一些,他是基于队列的任务管理工具,有任务关系、优先级、并发限制、任务取消等功能,相对于NSthread他封装的更好一些,也重一些。这2种方式基本满足了当时开发者的需求,简单开异步任务就用NSThread,任务之间存在关系就用NSOperation + NSOperationQueue来实现。

        2年后,出于降低能耗及更好的适配苹果的硬件等目的,随着iOS4的发布,苹果推出了GCD。GCD的能力基本和NSOperation类似,主要差异是他更轻量、灵活。GCD 提供了一个底层的并发框架,其中包含了线程池和队列调度等机制,用于更有效地管理并发任务。推出GCD的同时,苹果也顺带把NSOperationQueue的内部实现做了优化,应用了GCD的一些(或大部分)研发成果。所以可以根据个人习惯选择多线程工具。

NSThread

- (void)createAndStartThread {NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(runTask) object:nil];[thread start];
}- (void)runTask {NSLog(@"线程开始: %@", [NSThread currentThread]);// 模拟耗时任务[NSThread sleepForTimeInterval:1.0]; // 退出[NSThread exit];//NSLog(@"这行不会执行");
}

NSOperation

最基本就不写了,感觉和调用方法没啥区别

依赖关系
- (void)operationWithDependencies {NSLog(@"create operationWithDependencies on %@", NSThread.currentThread);NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"任务 1 执行 %@", NSThread.currentThread);}];NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"任务 2 执行 %@", NSThread.currentThread);}];// operation1 等待 operation2 完成[operation1 addDependency:operation2];NSOperationQueue *queue = [[NSOperationQueue alloc] init];[queue addOperations:@[operation1, operation2] waitUntilFinished:NO];NSLog(@"operationWithDependencies cimmit");
}

打印结果

create operationWithDependencies on <_NSMainThread: 0x60000170c000>{number = 1, name = main}

operationWithDependencies cimmit

任务 2 执行 <NSThread: 0x600001752500>{number = 4, name = (null)}

任务 1 执行 <NSThread: 0x600001763200>{number = 6, name = (null)}

如果我们设置了 waitUntilFinished YES,则相同于同步队列,程序会最后打印“operationWithDependencies cimmit

其他功能
  1. 设置最大并发数
  2. 取消所有队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 设置最多并发 2 个任务
queue.maxConcurrentOperationCount = 2;NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"任务1执行 %@", NSThread.currentThread);
}];NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"任务2执行 %@", NSThread.currentThread);
}];[queue addOperations:@[operation1, operation2] waitUntilFinished:YES];// 取消所有队列
[queue cancelAllOperations];

GCD(Grand Central Dispatch)

并发队列

异步任务在非主线程中执行

    dispatch_queue_t queue = dispatch_queue_create("com.example.myQueue", DISPATCH_QUEUE_CONCURRENT);// 同步任务(阻塞当前线程)dispatch_sync(queue, ^{NSLog(@"同步任务 - %@", [NSThread currentThread]);});// 异步任务(不阻塞当前线程)dispatch_async(queue, ^{NSLog(@"异步任务 - %@", [NSThread currentThread]);});
全局并发

各自线程不一致,无序

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{NSLog(@"全局并发队列任务1 - %@", [NSThread currentThread]);});dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{NSLog(@"全局并发队列任务2 - %@", [NSThread currentThread]);});dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{NSLog(@"全局并发队列任务3 - %@", [NSThread currentThread]);});
串行队列

以下代码推荐小白朋友反复执行观察规律。串行队列有个特点,加入队列的任务会严格按着加入顺序执行,无论他是同步还是异步。当同步任务被添加到队列后,程序会等待同步任务前加入的所有任务执行完毕,然后执行同步任务,然后继续往后执行。其中同步任务在主线程执行,异步任务在同一个子线程执行。

    dispatch_queue_t queue = dispatch_queue_create("com.example.barrier", nil);dispatch_async(queue, ^{NSLog(@"异步任务1 %@", NSThread.currentThread);});dispatch_async(queue, ^{NSLog(@"异步任务2 %@", NSThread.currentThread);});dispatch_sync(queue, ^{NSLog(@"同步任务 %@", NSThread.currentThread);});dispatch_async(queue, ^{NSLog(@"异步任务3 %@", NSThread.currentThread);});NSLog(@"主线程任务");

以上代码,4个任务的顺序是绝对不变的,主线程任务一定会在同步任务后执行,可能在3前,可能在3后,这事不确定的。

任务组 dispatch_group
   dispatch_group_t group = dispatch_group_create();// 自动入离队1dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{NSLog(@"任务1");});// 自动入离队2dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{NSLog(@"任务2");});// 手动入队dispatch_group_enter(group);dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@"延时任务完成");// 手动离队dispatch_group_leave(group);});dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"所有任务完成,回到主线程");});
栅栏任务
dispatch_queue_t queue = dispatch_queue_create("com.example.barrier", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{NSLog(@"任务1");
});dispatch_async(queue, ^{NSLog(@"任务2");
});dispatch_barrier_async(queue, ^{NSLog(@"栅栏任务");
});dispatch_async(queue, ^{NSLog(@"任务3");
});
信号量

可以用于控制并发数,实现锁的效果等

- (void)concurrentTaskControl {dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); // 最大并发数为2dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);for (int i = 0; i < 5; i++) {dispatch_async(queue, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 请求信号量NSLog(@"任务 %d 开始 - %@", i, [NSThread currentThread]);sleep(2);  // 模拟耗时任务NSLog(@"任务 %d 完成", i);dispatch_semaphore_signal(semaphore); // 释放信号量});}
}
其他场景

1、延迟执行dispatch_after,我经常用它解决一些定期权限检查之类的,就可以不用定时器

2、单例dispatch_once_t

3、并行遍历dispatch_apply

一些细节对比

1)任务组:DispatchGroup和NSOperationQueue类似,区别在于DispatchGroup提供的函数notify(queue:execute:)、enter()、leave()等,使得任务和队列之间的关系更灵活,不必强依赖于任务的状态。而OperationQueue中任务的整体管理依赖任务的状态,OperationQueue取消之后,如果任务还未开始执行,将不再执行;如果任务正在执行中,可继续执行,特殊情况需要终断的,可通过Operation的isCancelled属性来判断是否被取消了。

2)GCD的通过信号量来控制某个队列并发数,而OperationQueue用的是maxConcurrentOperationCount属性来控制,用起来差异不大,OperationQueue的代码更少、更直观。

3)GCD没有任务依赖关系的直接设置,但是可以通过栅栏(dispatchBarriers)来实现先后任务等待,NSOperation可以直接设置谁依赖谁,更直接。

4)GCD和NSOperation都能设置任务优先级,但是不要以为高优先级的任务就一定比低优先级的任务先执行,具体的执行时间还受到其他因素的影响,比如操作的依赖关系、操作的并发性设置等。

相关文章:

iOS主要知识点梳理回顾-2-多线程

iOS的多线程主要有三种方式&#xff0c;NSThread、GCD&#xff08;Grand Central Dispatch&#xff09;NSOperationQueue 开始&#xff0c;在iOS2发布的时候&#xff0c;苹果同步推出了NSthread和NSOperation。其中NSthread比较简单&#xff0c;仅提供了创建队列、开始、取消、…...

WPS如何接入DeepSeek(通过JS宏调用)

WPS如何接入DeepSeek 一、文本扩写二、校对三、翻译 本文介绍如何通过 WPS JS宏调用 DeepSeek 大模型&#xff0c;实现自动化文本扩写、校对和翻译等功能。 一、文本扩写 1、随便打开一个word文档&#xff0c;点击工具栏“工具”。 2、点击“开发工具”。 3、点击“查看代码”…...

【课程设计参考】迷宫小游戏 :基于 Python+Pygame+AI算法

一、内容 实现走迷宫 &#xff08;1&#xff09;游戏界面显示&#xff1a;迷宫地图、上下左右移动的特效。 &#xff08;2&#xff09;动作选择&#xff1a;上下左右键对应于上下左右的移动功能&#xff0c;遇到障碍的处理。 &#xff08;3&#xff09;得分统计功能&#xff…...

sa8295 qnx ais_camare如何支持一个摄像头两路vc输出?

当一个摄像头有两个vc输出的时候&#xff0c;如何更改驱动配置呢&#xff1f; 当一个摄像头可以输出两路vc&#xff0c;并且格式不同。根据每一路的vc图像数据格式修改串行器中maxxxx_mode_t里面的数组mode参数&#xff08;以下仅为例子&#xff09; struct maxxxx_mode_t ma…...

通过类加载和初始化的一些题目理解Java类加载过程

通过题目重点理解&#xff1a;Class加载流程和运行时区域 目录 子类和父类static变量父子类加载顺序2class.forName初始化 子类和父类static变量 class Parent {static int a 1;static int b 2;static int c;static {c 3;System.out.println("parent static block&quo…...

Coze(扣子)+ Deepseek:多Agents智能体协作开发新范式

前言 在当今数字化浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;技术的迅猛发展正深刻改变着我们的生活和工作方式。从智能语音助手到自动化流程机器人&#xff0c;AI 的应用无处不在&#xff0c;为我们提供了更加便捷、高效的服务。然而&#xff0c;对于非专业人士来…...

浅析Ruby类污染及其在Sinatra框架下的利用

和JavaScript中的原型链污染类似&#xff0c;Ruby中也存在类似的概念——类污染&#xff0c;两者都是对象进行不安全的递归合并导致的。 网上也没有相关的分析文章&#xff0c;只有下面这篇文章应该是第一次谈到这个问题 Class Pollution in Ruby: A Deep Dive into Exploiti…...

【NLP251】Transformer API调用

1. nn.Transformer nn.Transformer封装了Transformer中的包含编码器&#xff08;Encoder&#xff09;和解码器&#xff08;Decoder&#xff09;。如下图所示&#xff0c;它对Encoder和Decoder两部分的包装&#xff0c;它并没有实现输入中的Embedding和Positional Encoding和最…...

ubuntu下迁移docker文件夹

在 Ubuntu 系统中迁移 Docker 文件夹&#xff08;如 Docker 数据存储文件夹 /var/lib/docker&#xff09;到另一个磁盘或目录&#xff0c;通常是为了释放系统盘空间。以下是迁移过程的详细步骤&#xff1a; 1. 停止 Docker 服务 在进行迁移之前&#xff0c;必须停止 Docker 服…...

为AI聊天工具添加一个知识系统 之93 详细设计之34 Derivation 之 8 实现和平台

本文要点 要点 插入话题&#xff1a;实现 “实现”作为一个普通名词&#xff08;一般术语&#xff09;应该遵循第一性第二性第三性原则。其 第一性第二性第三性 分别是&#xff1a;完整性/鲁棒性/健壮性 &#xff0c;三者 分别注重 性能/功能/能力。即 首先是 实现完整性的性…...

idea 如何使用deepseek 保姆级教程

1.安装idea插件codegpt 2.注册deepseek并生成apikey deepseek 开发平台&#xff1a; DeepSeek​​​​​​​ 3.在idea进行codegpt配置 打开idea的File->Settings->Tools->CodeGPT->Providers->Custom OpenAI Chat Completions的URL填写 https://api.deepseek…...

python实现情绪识别模块,并将模块封装成可执行文件

目录&#xff1a; 1.源码&#xff1a;2.情绪识别模型运行流程&#xff1a;3.模型封装需要注意的地方&#xff1a;4.未解决问题&#xff1a; 1.源码&#xff1a; https://gitcode.com/xyint/deep_learning.git 2.情绪识别模型运行流程&#xff1a; 需要获取用户摄像头权限&…...

AH比价格策略源代码

用python 获取在A股和香港上市的公司和在A股和香港上市的公司股票代码和名称并且选出港股和A股涨幅相差比较大的股票 import akshare as akdef get_ah_stocks():# 获取A股股票列表a_stock_list ak.stock_zh_a_spot_em()print(a_stock_list)a_stock_list a_stock_list[[&quo…...

trimesh 加载obj mesh处理

目录 trimesh 加载obj trimesh入门 主要功能 安装 基本用法 1. 加载和保存 3D 模型 2. 几何操作 3. 网格分析 4. 可视化 5. 布尔运算 6. 碰撞检测 trimesh 加载obj template_mesh trimesh.load_mesh(r"E:\project\3d\lilpotat--pytorch3d\pixie_data\smplx_te…...

常见数据结构的C语言定义---《数据结构C语言版》

文章目录 1. 静态分配的顺序表2. 动态分配的顺序表3. 单 链 表4. 双 链 表5. 静态链表6. 顺序栈7. 链栈8. 顺序存储的队列9. 链式存储的队列10. 链式存储的二叉树11. 线索二叉树12. 树的双亲表示法13. 树的孩子兄弟表示法12. 图的邻接矩阵法13. 图的邻接表法1-13集合版本 #defi…...

C++小知识记录,不定时更新

1. 普通函数不能在头文件中定义&#xff1a; 当多个.cpp调用时&#xff0c;在编译链接时会在.o文件中重复定义报错 2. 为什么内联函数可以在头文件中定义&#xff1a;适用短小函数 当.cpp调用时&#xff0c;编译器只会在当前文件展开该函数&#xff0c;相当于每个.cpp会重新定…...

python--sqlite

1. 连接到数据库 使用 sqlite3.connect() 方法可以创建一个到SQLite数据库的连接。如果指定的数据库文件不存在&#xff0c;它会自动创建一个新的数据库文件。 import sqlite3# 连接到数据库&#xff0c;如果数据库文件不存在则会创建一个新的 conn sqlite3.connect(example…...

使用 Axios ——个人信息修改与提示框实现

目录 详细介绍&#xff1a;个人信息设置与修改页面实现 1. HTML 结构 2. CSS 样式 3. JavaScript 核心逻辑 a. 信息渲染与表单提交 b. 头像上传与预览 4. 功能详解 5. 总结 提示&#xff1a; 这段代码展示了如何创建一个简单的个人信息设置页面&#xff0c;包含用户个…...

群晖安装Gitea

安装Docker Docker运行Gitea 上传gitea包&#xff0c;下载地址&#xff1a;https://download.csdn.net/download/hmxm6/90360455 打开docker 点击印象&#xff0c;点击新增&#xff0c;从文件添加 点击启动 可根据情况&#xff0c;进行高级设置&#xff0c;没有就下一步 点击应…...

LabVIEW商业软件开发

在商业软件开发和仪器自动测试领域&#xff0c;LabVIEW以其图形化编程方式、高效的数据采集能力和强大的硬件集成优势&#xff0c;成为众多工程项目的核心开发工具。然而&#xff0c;商业软件的开发远不止编写代码和实现功能那么简单&#xff0c;尤其是在仪器自动测试领域&…...

内容中台赋能人工智能技术提升业务创新能力

内容概要 在当今快速变化的市场环境中&#xff0c;企业需要不断寻求创新以保持竞争力。内容中台作为一种新型的内容管理架构&#xff0c;能够极大地提升企业在内容创建、管理和分发方面的效率。通过与人工智能技术的深度融合&#xff0c;企业能够将海量的数据和信息转化为有价…...

spring 基于构造方法实例化对象

在 spring 中&#xff0c;对象的实例化创建都在 AbstractAutowireCapableBeanFactory#createBeanInstance 方法中完成&#xff0c;其中定义了不少实例化策略&#xff0c;如&#xff1a;Supplier、工厂方法、构造方法、无参构造。其中无参构造&#xff0c;即 AbstractAutowireCa…...

生成式聊天机器人 -- 基于Pytorch + Global Attention + 双向 GRU 实现的SeqToSeq模型 -- 下

生成式聊天机器人 -- 基于Pytorch Global Attention 双向 GRU 实现的SeqToSeq模型 -- 下 训练Masked 损失单次训练过程迭代训练过程 测试贪心解码(Greedy decoding)算法实现对话函数 训练和测试模型完整代码 生成式聊天机器人 – 基于Pytorch Global Attention 双向 GRU 实…...

Vue.js 与第三方插件的集成

Vue.js 与第三方插件的集成 今天我们来聊聊如何在 Vue 项目中集成第三方插件。随着项目功能不断增多&#xff0c;我们常常需要引入各种第三方库和插件&#xff0c;比如国际化、图表、日期处理等&#xff0c;来提升开发效率和用户体验。下面就跟大家分享一下集成第三方插件的常…...

Netty初学九 心跳与空闲检测

一、网络问题 1.连接假死&#xff1a; 连接假死的现象是&#xff1a;在某一端看来&#xff0c;底层的Tcp连接已经断开&#xff0c;但是应用程序没有捕获到&#xff0c;会认为这条连接仍然是存在的。从TCP层面来说&#xff0c;只有收到四次握手数据包或者一个RST数据包才可以表示…...

数据分析如何做EDA

探索性数据分析&#xff08;EDA&#xff0c;Exploratory Data Analysis&#xff09;是数据分析过程中至关重要的一步&#xff0c;其目的是通过统计和可视化技术对数据进行初步分析&#xff0c;从而揭示数据的潜在模式、特征和异常值&#xff0c;并为后续的数据预处理、特征工程…...

AD域控粗略了解

一、前提 转眼大四&#xff0c;目前已入职上饶一公司从事运维工程师&#xff0c;这与我之前干的开发有着很大的差异&#xff0c;也学习到了许多新的知识。今天就写下我对于运维工作中常用的功能——域控的理解。 二、为什么要有域控&#xff0c;即域控的作用 首先我们必须要…...

【计算机网络】TCP/IP 网络模型有哪几层?

目录 应用层 传输层 网络层 网络接口层 总结 为什么要有 TCP/IP 网络模型&#xff1f; 对于同一台设备上的进程间通信&#xff0c;有很多种方式&#xff0c;比如有管道、消息队列、共享内存、信号等方式&#xff0c;而对于不同设备上的进程间通信&#xff0c;就需要网络通…...

使用 Typora 编写 MD 文档:从入门到精通(附赠百度网盘下载地址)

引言 Markdown&#xff08;简称 MD&#xff09;是一种轻量级标记语言&#xff0c;广泛应用于技术写作、博客撰写、文档管理等领域。Typora 是一款功能强大且易于使用的 Markdown 编辑器&#xff0c;支持实时预览、语法高亮、主题切换等功能&#xff0c;深受开发者和写作者的喜…...

Spring统一修改RequestBody

我们编写RestController时&#xff0c;有可能多个接口使用了相同的RequestBody&#xff0c;在一些场景下需求修改传入的RequestBody的值&#xff0c;如果是每个controller中都去修改&#xff0c;代码会比较繁琐&#xff0c;最好的方式是在一个地方统一修改&#xff0c;比如将he…...