【iOS】GCD学习

news/2024/10/29 3:37:08/

GCD的概念

GCD(Grand Central Dispatch),是有Apple公司开发的一个多核编程的解决方案,用以优化应用程序支持多核处理器,是基于线程模式之上执行并发任务。

GCD的优点

  1. 利用设备多核进行并行运算
  2. GCD自动充分使用设备的CPU内核
  3. GCD自动管理线程的生命周期(线程创建、线程调度、线程销毁)
  4. 使用简单

GCD任务和队列

任务

任务就是执行操作,即可以执行的代码;执行任务有两种方式:同步 和 异步。

  • 同步执行: 同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。不具备开启新线程的能力。
  • 异步执行:异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。可以在新的线程中执行任务,具备开启新线程的能力。

队列

Dispatch Queue指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,遵循 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务;在 GCD 中有两种队列:串行队列 和 并发队列。两者都符合 FIFO(先进先出)的原则。两者的主要区别是:执行顺序不同,以及开启线程数不同。

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

CGD使用步骤

  1. 创建队列
  2. 将任务追加到任务的等待队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)。

创建队列的方法

  1. 可以使用 dispatch_queue_create 方法来创建队列。该方法需要传入两个参数:
  • 第一个参数表示队列的唯一标识符,用于 DEBUG,可为空。队列的名称推荐使用应用程序 ID 这种逆序全程域名。
  • 第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL 表示串行队列,DISPATCH_QUEUE_CONCURRENT 表示并发队列。
// 串行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
  1. 对于串行队列,GCD 默认提供了:『主队列(Main Dispatch Queue)』。
  • 所有放在主队列中的任务,都会放到主线程中执行。
  • 可使用 dispatch_get_main_queue() 方法获得主队列。
// 主队列的获取方法
dispatch_queue_t queue = dispatch_get_main_queue();

创建任务的方法

GCD 提供了同步执行任务的创建方法 dispatch_sync 和异步执行任务创建方法 dispatch_async。

同步任务: dispatch_sync:

 // 同步执行任务创建方法dispatch_sync(dispatch_get_global_queue(0, 0), ^{NSLog(@"%s",__func__);});

异步任务: dispatch_async

  // 异步执行任务创建方法dispatch_async(dispatch_get_global_queue(0, 0), ^{NSLog(@"%s",__func__);});

任务和队列不同组合方式的区别:

区别并发队列串行队列主队列
同步(sync)没有开启新线程,串行执行任务没有开启新线程,串行执行任务死锁卡住不执行
异步(async有开启新线程,并发执行任务有开启新线程(1条),串行执行任务没有开启新线程,串行执行任务

注意:从上边可看出: 『主线程』 中调用 『主队列』+『同步执行』 会导致死锁问题。
这是因为 主队列中追加的同步任务 和 主线程本身的任务 两者之间相互等待,阻塞了 『主队列』,最终造成了主队列所在的线程(主线程)死锁问题。
而如果我们在 『其他线程』 调用 『主队列』+『同步执行』,则不会阻塞 『主队列』,自然也不会造成死锁问题。最终的结果是:不会开启新线程,串行执行任务。

CGD的组合使用

串行队列 + 同步执行

不会开启新线程,在当前线程执行任务。执行完一个任务,再执行下一个任务。

  /*** 串行队列 + 同步执行* 特点:不会开启新线程,在当前线程执行任务。执行完一个任务,再执行下一个任务。*/dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL);dispatch_sync(queue, ^{// 追加任务 1[NSThread sleepForTimeInterval:1];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_sync(queue, ^{// 追加任务 2[NSThread sleepForTimeInterval:3];              // 模拟耗时操作NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_sync(queue, ^{// 追加任务 3[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程});NSLog(@"syncSerial---end");

运行结果:
在这里插入图片描述

串行队列 + 异步执行

会开启新线程。执行完一个任务,再执行下一个任务。

 /*** 串行队列 + 异步执行* 特点:会开启新线程。执行完一个任务,再执行下一个任务。*/dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);dispatch_async(queue, ^{// 任务 1[NSThread sleepForTimeInterval:1];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_async(queue, ^{// 任务 2[NSThread sleepForTimeInterval:3];              // 模拟耗时操作NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_async(queue, ^{// 任务 3[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程});NSLog(@"asyncSerial---end");

运行结果:
在这里插入图片描述

并发队列 + 同步执行

不会开启新线程,在当前线程执行任务。执行完一个任务,再执行下一个任务。

  /*** 并发队列 + 同步执行* 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。*/dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_sync(queue, ^{// 任务 1[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_sync(queue, ^{// 任务 2[NSThread sleepForTimeInterval:3];              // 模拟耗时操作NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_sync(queue, ^{// 任务 3[NSThread sleepForTimeInterval:1];              // 模拟耗时操作NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程});NSLog(@"syncConcurrent---end");

运行结果:
在这里插入图片描述

并发队列 + 异步执行

可以开启多个线程,任务同时执行。

   /*** 并发队列 + 异步执行* 特点:可以开启多个线程,任务同时执行。*/dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{// 任务 1[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_async(queue, ^{// 任务 2[NSThread sleepForTimeInterval:3];              // 模拟耗时操作NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_async(queue, ^{// 任务 3[NSThread sleepForTimeInterval:1];              // 模拟耗时操作NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程});NSLog(@"asyncConcurrent---end");

运行结果:
在这里插入图片描述

同步执行 + 主队列

在主线程中调用 『同步执行 + 主队列』

互相等待卡住不可行

/*** 同步执行 + 主队列* 特点(主线程调用):互等卡主不执行。* 特点(其他线程调用):不会开启新线程,执行完一个任务,再执行下一个任务。*/
- (void)syncMain {NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程NSLog(@"syncMain---begin");dispatch_queue_t queue = dispatch_get_main_queue();dispatch_sync(queue, ^{// 追加任务 1[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_sync(queue, ^{// 追加任务 2[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_sync(queue, ^{// 追加任务 3[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程});NSLog(@"syncMain---end");
}

在其他线程中调用『同步执行 + 主队列』

不会开启新线程,执行完一个任务,再执行下一个任务

// 使用 NSThread 的 detachNewThreadSelector 方法会创建线程,并自动启动线程执行 selector 任务
[NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];

异步执行 + 主队列

只在主线程中执行任务,执行完一个任务,再执行下一个任务。

/*** 异步执行 + 主队列* 特点:只在主线程中执行任务,执行完一个任务,再执行下一个任务*/
- (void)asyncMain {NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程NSLog(@"asyncMain---begin");dispatch_queue_t queue = dispatch_get_main_queue();dispatch_async(queue, ^{// 追加任务 1[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_async(queue, ^{// 追加任务 2[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程});dispatch_async(queue, ^{// 追加任务 3[NSThread sleepForTimeInterval:2];              // 模拟耗时操作NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程});NSLog(@"asyncMain---end");
}

结果:
在这里插入图片描述


http://www.ppmy.cn/news/57287.html

相关文章

​【五一创作】基于mysql关系型实现分布式锁

看完该文预计用时:15分钟 看之前应具体的技术栈:springboot mysql nginx(了解即可) 目录 0.写在前面 1. 从减库存聊起 1.1. 环境准备 1.2. 简单实现减库存 1.3. 演示超卖现象 1.4. jvm锁问题演示 1.4.2. 原理 1.5. 多服务问…

【51单片机】数码管显示(样例展示以及异常分析)

🎊专栏【51单片机】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 ⭐数码管 比如要显示“6”,那么下面图片中,AFEDCG=1,B=0 对应到数码管上,就是 ⭐原理 🎊P22~P24控制LED1~

transformer and DETR

RNN 很难并行化处理 Transformer 1、Input向量x1-x4分别乘上矩阵W得到embedding向量a1-a4。 2、向量a1-a4分别乘上Wq、Wk、Wv得到不同的qi、ki、vi(i{1,2,3,4})。 3、使用q1对每个k(ki)做attention得到a1,i(i{1,2,3,4…

FPGA时序约束(六)时序例外约束

系列文章目录 FPGA时序约束(一)基本概念入门及简单语法 FPGA时序约束(二)利用Quartus18对Altera进行时序约束 FPGA时序约束(三)时序约束基本路径的深入分析 FPGA时序约束(四)主时…

了解标量、向量和点积

数据科学基础数学:线性代数简介 了解标量、向量和点积 机器只能按着算法理解和处理数据结构存储的数字. 例如创建垃圾邮件检测器,则首先必须将文本数据转换为数字(通过单词嵌入)。 两个句子之间的余弦相似性 两个句子之间的余弦相似性可以通过它们的向量…

2023前端大厂高频面试题之项目篇

系列文章: 2023前端大厂面试题之JavaScript篇(1) 2023前端大厂面试题之JavaScript篇(2) 2023前端大厂面试题之JavaScript篇(3) 2023前端大厂面试题之JavaScript篇(4) 2023前端大厂高频面试题之CSS篇 2023前端大厂高频面试题之Vue篇(1) 2023前端大厂高频面试题之Vue篇…

【数据库复习】第四章数据库保护 1

数据库安全性: 数据库的一大特点是数据可以共享 数据共享必然带来数据库的安全性问题 数据库系统中的数据共享不能是无条件的共享 用户标识与鉴别 用户名和口令易被窃取,每个用户预先约定好一个计算过程或者函数 存取控制 常用存取控制方法 自主存…

第13章 项目合同管理

文章目录 13.2.1 按信息系统 范围 划分的合同分类 4451、总承包合同2、单项工程承包合同3、分包合同 13.2.2 按项目 付款方式 划分的合同分类 4461、总价合同2、成本补偿合同(卖方有利)3、工料合同 13.3.1 项目合同的内容 44713.3.2 项目合同签订的注意事…