【iOS】GCD学习

news/2024/7/20 22:04:41 标签: ios, 学习, objective-c

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.niftyadmin.cn/n/281999.html

相关文章

碳中和专题:智慧城市建设能否提高碳生产率?—基于中国智慧城市试点的准自然实验

一.研究内容 目前,中国面临着如何在保证经济增长的前提下实现减排目标的困境。智能城市建设作为一种新型的城市发展模式,有可能成为解决这一问题的关键。本文使用多周期双重差分DID模型进行实证分析,旨在评估智能城市试点政策对碳生产率的影响…

计算机网络笔记:TCP协议 和UDP协议(传输层)

TCP 和 UDP都是传输层协议,他们都属于TCP/IP协议族。 TCP 基本概念 TCP的全称是传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP 是面向连接的、可靠的流协议(流就是指不间断的数据结构) TCP报文格式 TCP报文是…

存储资源调优技术——SmartVirtualization异构虚拟化技术

目录 基本概念 相关专业术语 eDevLUN与外部LUN的关系 对异构存储系统接管的方式 基本概念 异构虚拟化技术,仅对块业务生效 当本端存储系统与异构存储系统相互连接后;本端存储系统能够将异构存储系统提供的存储资源当作本地存储资源进行使用并对其进行集中…

入门大纲 我为什么使用delta-io 数据湖 替代hive

1 大厂背书 databricks宣布把delta-io共享给apache基金会 并且delta-io从以前打杂的0.x版本升级为1.x 随后就是bug的各种修复和新功能的增加. release note可以看: Releases delta-io/delta GitHub 2 并发控制(解决了多任务并发读写表时的 读写冲突) hive/spark 如果多个任…

Spring:Bean的实例化(构造方法、静态工厂和实例化工厂)

三种方式&#xff0c;分别为构造方法、静态工厂和实例化工厂 新建Module项目&#xff0c;选择Maven&#xff0c;在pom.xml导入如下依赖&#xff1a; pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.o…

Hadoop大数据分析技术(伪分布式搭建)

一.安装JDK和配置SSH免密登录 &#xff08;1&#xff09;准备软件 &#xff08;2&#xff09;解压压缩包 tar -zxvf jdk-8u221-linux-x64.tar.gz &#xff08;3&#xff09;在此处我们配置系统环境变量&#xff0c;使用命令&#xff1a; vim /etc/profile &#xff08;4&#x…

机器学习框架教程:介绍一些流行的机器学习框架(如Scikit-learn、XGBoost等)

一、引言 机器学习框架的意义与作用 随着人工智能的发展&#xff0c;机器学习已经成为一种重要的技术手段&#xff0c;解决了许多实际问题。在实际应用中&#xff0c;数据科学家需要花费大量时间和精力进行数据预处理、特征工程、模型训练、评估以及优化等任务。为了降低这些…

[230503] 2021年托福阅读真题第1篇|Grinding Grain 磨粒

11:21&#xff5e;11:41 慢 20min 正确率&#xff1a;6.5/10 题目来源&#xff1a;链接: https://pan.baidu.com/s/15FYCuD7__slfGvdsBIHgLQ 提取码: iynj --来自百度网盘超级会员v5的分享【内含2021年100篇托福阅读真题】 目录 Grinding Grain 题目 Grinding Grain It now…