iOS - 多线程-GCD

文章目录

  • iOS - 多线程-GCD
    • 1. 常见多线程方案
    • 2. GCD
      • 2.1 GCD的常见函数
        • GCD中有2个用来执行任务的函数
      • 2.2 GCD的队列
        • 2.2.1 GCD的队列可以分为2大类型
      • 2.3 容易混淆的术语
        • 2.4.1 有4个术语比较容易混淆:`同步、异步`、`并发、串行`
      • 2.4 各种队列的执行效果
    • 3. 死锁
      • 3.1 死锁示例
      • 3.2 死锁分析
      • 3.3 其他示例
      • 3.3.1 interview02
      • 3.3.2 interview03
      • 3.3.3 interview04
    • 4. 案例
      • 4.1 案例1
      • 4.2 分析
    • 5. 拓展
      • GNUstep

iOS - 多线程-GCD

1. 常见多线程方案

NSThreadGCDNSOperation底层都依赖于pthread

2. GCD

2.1 GCD的常见函数

GCD中有2个用来执行任务的函数
  • 同步的方式执行任务
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

    1. queue:队列
    2. block:任务
  • 异步的方式执行任务
    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

  • GCD源码:https://github.com/apple/swift-corelibs-libdispatch

2.2 GCD的队列

2.2.1 GCD的队列可以分为2大类型
  • 并发队列(Concurrent Dispatch Queue)

    1. 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
    2. 并发功能只有在异步dispatch_async)函数下才有效
  • 串行队列(Serial Dispatch Queue)

    1. 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

2.3 容易混淆的术语

2.4.1 有4个术语比较容易混淆:同步、异步并发、串行
  • 同步异步主要影响:能不能开启新的线程

    • 同步:在当前线程中执行任务,不具备开启新线程的能力
    • 异步:在新的线程中执行任务,具备开启新线程的能力
  • 并发串行主要影响:任务的执行方式

    • 并发多个任务并发(同时)执行
    • 串行一个任务执行完毕后,再执行下一个任务

2.4 各种队列的执行效果

  • 使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)

3. 死锁

3.1 死锁示例

// 问题:以下代码是在主线程执行的,会不会产生死锁?
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
   NSLog(@"执行任务2");
});
NSLog(@"执行任务3");

3.2 死锁分析

如上代码

  • 正常情况应该是顺序执行任务1任务2任务3
  • 任务2使用dispatch_sync同步执行方式,放入主线程队列,因此任务2需要排队等待前面的任务执行完成后才执行
  • 但是当前方法体viewDidLoad可以认为就是一个任务在执行,但是执行到任务2dispatch_sync处,会等待dispatch_sync执行完成再继续往下执行
  • 此时,相当于任务2等待当前执行任务执行完成,当前执行任务也在等待任务2执行完成,相互等待因此造成线程死锁

3.3 其他示例

3.3.1 interview02

- (void)interview02 {
    // 问题:interview01中的sync,改成 async。会不会产生死锁?不会!
    /* 打印日志:
        执行任务1
        执行任务3
        执行任务2
     */
    NSLog(@"执行任务1");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"执行任务2");
    });
    NSLog(@"执行任务3");
}

3.3.2 interview03

- (void)interview03 {
    // 会不会产生死锁?会!
    /*
     分析:
     执行任务2 后,同步等待任务2 执行,但是因为queue是串行的,所以会相互等待
     */
    NSLog(@"执行任务1");
    dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"执行任务2");
        dispatch_sync(queue, ^{
            NSLog(@"执行任务3");
        });
        NSLog(@"执行任务4");
    });
    NSLog(@"执行任务5");
}

3.3.3 interview04

- (void)interview04 {
    // interview03改为并发队列(DISPATCH_QUEUE_CONCURRENT),会不会死锁?不会!
    NSLog(@"执行任务1");
    dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"执行任务2");
        dispatch_sync(queue, ^{
            NSLog(@"执行任务3");
        });
        NSLog(@"执行任务4");
    });
    NSLog(@"执行任务5");
}

4. 案例

4.1 案例1

如下代码打印什么:

- (void)test {
    NSLog(@"2");
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        NSLog(@"1");
        [self performSelector:@selector(test) withObject:nil afterDelay:0];
        NSLog(@"3");
    });
}

打印结果:

观察到,2不会打印

去掉dispatch_async又会怎么样

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"1");
    [self performSelector:@selector(test) withObject:nil afterDelay:0];
    NSLog(@"3");
}


这时候都有打印,只不过打印顺序1>3>2

接着,回到dispatch_async里执行,但是把afterDelay去掉

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        NSLog(@"1");
        [self performSelector:@selector(test) withObject:nil];
        NSLog(@"3");
    });
}


打印结果是1>2>3,这次打印顺序是正常的

4.2 分析

上面的例子中,主要是考察runloop多线程的相关知识

  • 首先,在dispatch_async中使用- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;方法来执行,使用afterDelay:0看似是在没有延迟的情况下执行,实际上因为该方法是基于 runloop的,相当于往runloop添加一个定时器,但是因为此时我们是在子线程中执行的,子线程中的runloop默认不会开启,所以test方法没有执行。我们尝试开启runloop
dispatch_async(queue, ^{
    NSLog(@"1");
    [self performSelector:@selector(test) withObject:nil afterDelay:0];
    NSLog(@"3");
    
    [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
});

可以看到,2打印了

  • 接着,我们去掉了dispatch_async,发现打印结果是1>3>2,为什么3会比2先打印的?
    使用afterDelay:0看似是在没有延迟的情况下执行,实际上因为该方法是基于 runloop的定时器,虽然没有延迟设置为 0,但是runloop的定时器是在被唤醒的时候处理定时器的,但是在进入休眠之前会处理完点击事件,因此看到的打印结果是13先打印,然后打印2

  • 最后,回到dispatch_async中执行,只不过使用的是- (id)performSelector:(SEL)aSelector withObject:(id)object;方法来执行,查看源码

    该方法实际是直接使用objc_msgSend方法执行,相当于我们直接[self test]这样调用方法,所以这时候打印顺序是正常的1>2>3

5. 拓展

GNUstep

GNUstep是GNU计划的项目之一,它将Cocoa的OC库重新开源实现了一遍

源码地址:https://gnustep.github.io/resources/downloads.html

虽然GNUstep不是苹果官方源码,但还是具有一定的参考价值

@oubijiexi

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

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

相关文章

了解BACnet的对象模型 (三)

文章目录 前言18个对象BACnet 对象的属性设备对象&#xff08;Device&#xff09;的属性输入输出值对象类型及其属性 在代码中的表达Device对象的属性模拟输入对象的属性 小结 前言 在楼宇自控网络中&#xff0c;各种设备之间要进行数据交换&#xff0c;为了能够实现设备的互操…

【数字电路与系统】【北京航空航天大学】实验:时序逻辑设计——三色灯开关(四)、设计实现

本次实验&#xff08;一&#xff09;见博客&#xff1a;【数字电路与系统】【北京航空航天大学】实验&#xff1a;时序逻辑设计——三色灯开关&#xff08;一&#xff09;、实验指导书 本次实验&#xff08;二&#xff09;见博客&#xff1a;【数字电路与系统】【北京航空航天…

【Yolov系列】Yolov5学习(一)补充1.1:自适应锚框计算

1、Yolov5的网络结构 Yolov5中使用的Coco数据集输入图片的尺寸为640*640&#xff0c;但是训练过程的输入尺寸并不唯一&#xff0c;Yolov5可以采用Mosaic增强技术把4张图片的部分组成了一张尺寸一定的输入图片。如果需要使用预训练权重&#xff0c;最好将输入图片尺寸调整到与作…

表达式求值(后缀表达式)(数据结构)

一、概念 算术表达式是由操作数&#xff08;运算数&#xff09;、运算符&#xff08;操作符&#xff09;、和界线符&#xff08;括号&#xff09;三部分组成&#xff0c;在计算机中进行算术表达式的计算是通过堆栈来实现的。 二后缀表达式的逻辑和实现方式&#xff08;逆波兰…

CST电磁仿真软件波导端口和离散端口的设置流程【教程解读】

置波导端口 通过波导端口馈电! Simulation > Sources and Loads > Waveguide Port 假设Waveguide Port是无限长的波导上给定的Mode&#xff0c;用来激励电磁场。相比离散端口(Discrete Port)&#xff0c;波导端口可以提供很好的模式匹配&#xff0c;因此能提供更高的精…

Android Material Design学习笔记

Material Design控件学习记录 Toolbar 新建一个工程后&#xff0c;在res/values/themes.xml文件里 <resources xmlns:tools"http://schemas.android.com/tools"><!-- Base application theme. --><style name"Theme.MaterialTest" paren…

Python的round与Excel的round不一样?

Python四舍五入怎么做 round()奇进偶舍round函数既不是“四舍五入”的原则&#xff0c;也不是“四舍六入无成双”的原则。 decimal round() 偶然发现python的round函数和excel的round函数对某些数据的处理结果不一致。有看到博主提到是奇进偶舍的方法&#xff0c;但经过验证和…

深度学习与神经网络入门

前言 人工智能&#xff08;AI&#xff09;与机器学习&#xff08;ML&#xff09;与深度学习&#xff08;DL&#xff09;的关系&#xff1a; DL包含于ML&#xff0c;ML包含于AI。 即深度学习是机器学习一部分&#xff0c;机器学习又是人工智能的一个分支。 那么深度学习到底有…

关系型数据库的相关概念

表、记录、字段 表 一个实体集相当于一个表记录 一个实体相当于一个记录&#xff0c;在表中表表现为一行数据字段 一个字段相当于数据库表中的列 表的关联关系 一对一(一对一的表可以合并成一张表)一对多多对多 必须创建第三张表&#xff0c;该表通常称为联接表&#xff0c…

协议的定制之序列化与反序列化 | 守护进程

目录 一、再谈协议 二、序列化与反序列化 三、网络计算器的简单实现 四、网络计算器完整代码 五、代码改进 六、守护进程 七、Json序列化与反序列化 八、netstat 一、再谈协议 是对数据格式和计算机之间交换数据时必须遵守的规则的正式描述。简单的说了&#xff0c;网络…

java多线程-练习

需求 代码 使用Callable 方便返回执行的结果&#xff1a;每个线程的发送礼物的数量礼物清单&#xff1a;共享资源&#xff0c;方便上锁礼物数量&#xff1a;线程变量&#xff0c;每个线程发送礼物的数量 public class SendGiftThread implements Callable<Integer> {// 共…

通过命令行构建Django应用程序

假设读者安装好Django开发环境后&#xff08;这个环境搭建很容易&#xff0c;大家可以参看随意一个网文&#xff09;&#xff0c;就可以通过命令行构建Django应用程序了。通过命令行构建Django应用程序的关键&#xff0c;是使用一个Django框架自带的管理工具——django-admin.p…

springboot3 集成knife4j

knife4j介绍 Knife4j是一个集Swagger2 和 OpenAPI3为一体的增强解决方案。 springdoc地址&#xff1a;OpenAPI 3 Library for spring-boot Knife4j官网地址&#xff1a;Knife4j 集Swagger2及OpenAPI3为一体的增强解决方案. | Knife4j 环境介绍 java&#xff1a;17 Spring…

你们项目日志是如何处理的???

ELK日志采集系统 1.什么是ELK ELK 是一套流行的数据搜索、分析和可视化解决方案&#xff0c;由三个开源项目组成&#xff0c;每个项目的首字母合起来形成了“ELK”这一术语&#xff1a; Elasticsearch (ES): Elasticsearch 是一个基于 Apache Lucene 构建的分布式、实时搜索与…

项目开发流程

项目开发流程 &#x1f469;‍&#x1f9b3;项目立项 估计项目的花费&#xff0c;确定大致的所需开发人员数&#xff0c;确定项目是否可行&#xff1b; &#x1f469;‍&#x1f9b0;需求分析 整体过程&#xff1a; 项目背景和目标&#xff0c;即项目的目的是什么 用户需求&…

动态Web项目讲解+Demo

web流程演示 请求路径 请求路径明确要请求的是哪个servlet 请求方式 servlet含有两种请求方式&#xff1a;doGet和doPost doGet&doPost 返回数据就是httpResponse&#xff0c;返回给success 参数 包含在request当中 成功 上述流程任何一步都没出问题&#xff0c;就会…

设计模式之创建型模式---工厂模式

文章目录 工厂模式概述简单工厂简单工厂的代码实现简单工厂的使用简单工厂应用场景 工厂方法工厂方法模式的代码实现工厂方法的使用工厂方法应用场景 抽象工厂抽象工厂模式代码实现抽象工厂的使用方法抽象工厂模式的应用场景 总结 工厂模式概述 工厂模式从名字就能看出&#x…

在【laravel框架】学习中遇到的常见的问题以及解决方法

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

.net core webapi 高颜值的接口管理系统界面取代swagger,更好调试和查看

.net core webapi 高颜值的接口管理系统界面取代swagger&#xff0c;更好调试和查看 安装 dotnet add package IGeekFan.AspNetCore.Knife4jUI --version 0.0.16配置文档&#xff1a; 配置起始页 builder.Services.AddSwaggerGen(c > {// 配置 Swagger 文档相关信息c.Swa…

开源项目实现简单实用的股票回测

1 引言 之前&#xff0c;尝试做股票工具一直想做的大而全&#xff0c;试图抓取长期的各个维度数据&#xff0c;然后统计或者训练模型。想把每个细节做到完美&#xff0c;结果却陷入了细节之中&#xff0c;最后烂尾了。 最近&#xff0c;听到大家分享了一些关于深度学习、时序…