美文网首页@IT·互联网
GCD我们应该知道些什么

GCD我们应该知道些什么

作者: 黑炭长 | 来源:发表于2024-03-11 11:40 被阅读0次

本篇主要记录三个问题
1、GCD面试题分享
2、GCD相关的死锁
3、串行并发底层分析

1、GCD面试题分享

首先我们说一下什么是GCD

  • GCD的全称是Grand Central Diapatch,是纯C语言编写的,提供了跟多强大的函数
  • GCD的优势:
    GCD是Apple公司提出的多核的并行运算提出的解决方案
    GCD会自动利用更多的CPU内核
    会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
  • 任务使用block封装
  • 任务的block没有参数 也没有返回值
  • 执行任务的函数
    *异步 dispatch_async
    不用等待当前语句执行完成 即可执行下条任务
    会开启线程执行block任务
    异步是多线程的代名词
    *同步 dispatch_sync
    必须等待当前任务完成 才会执行下一条任务
    不会开启线程
    在当前block中执行任务
  • 队列
    *串行队列,先进先出,顺序执行 DQF_WIDTH = 1
    *并发队列 ,多个任务同时执行 DQF_WIDTH = MAX,任务完成时间不确定 要看任务的复杂度
  • 线程和队列 可以认为没有直接的关系,但是线程和队列同处于同一个进程中,会相互影响,同时同一个任务执行的快慢 与线程和队列都有关系
    线程的状态
    CPU的调度
    队列的优先级
    任务的复杂度,都会影响任务的执行
  • 串行队列
dispatch_queue_create("queue.name", DISPATCH_QUEUE_SERIAL);

所有任务按顺序依次执行,结束顺序固定,符合先进先出的基本原则,队列后面的任务必须等待前面的任务执行完毕后才出队列。但是,不要认为串行队列中的所有任务都在同一个线程中执行,串行队列中的异步任务,可能会开启新线程去执行。

  • 并发队列
dispatch_queue_create("queue.name", DISPATCH_QUEUE_CONCURRENT);

所有任务可以同时执行,结束顺序不固定,只要有可用线程,则队列头部任务将持续出队列。

  • 主队列
dispatch_get_main_queue()

本质是一个特殊的串行队列,主队列的任务都在主线程来执行,专门负责调度主线程度的任务,无法开辟新的线程。所以,在主队列下的任务不管是异步任务还是同步任务都不会开辟线程,任务只会在主线程顺序执行。如果在主线程上已经有任务正在执行,主队列会等到主线程空闲后再调度任务。

  • 全局队列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

本质是一个特殊的并发队列。在后面加入了“服务质量”和“调度优先级” 两个参数

  • 串行队列 + 异步,顺序执行,先进先出,可能会开启新的线程
//串行队列
dispatch_queue_t serial = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
//异步任务
for (int i=0; i<5; i++) {

    dispatch_async(serial, ^{

        NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
        if (i==1 || i==3) {
            [NSThread sleepForTimeInterval:2];
        }
    });
}
NSLog(@"=============== 所有任务执行完毕 =====");

执行结果:
2022-05-17 15:27:35.976433+0800 myDemo[278:9179] =============== 所有任务执行完毕 =====
2022-05-17 15:27:35.976538+0800 myDemo[278:9420] 这是第 0 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:35.976655+0800 myDemo[278:9420] 这是第 1 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:37.978072+0800 myDemo[278:9420] 这是第 2 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:37.978150+0800 myDemo[278:9420] 这是第 3 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}
2022-05-17 15:27:39.987439+0800 myDemo[278:9420] 这是第 4 个任务;线程 <NSThread: 0x283628500>{number = 4, name = (null)}

  • 串行队列 + 同步,顺序执行,先进先出,不会开启新的线程
//串行队列
dispatch_queue_t serial = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
//同步任务
for (int i=0; i<5; i++) {

    dispatch_sync(serial, ^{

        NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
        if (i==1 || i==3) {
            [NSThread sleepForTimeInterval:2];
        }
    });

执行结果:

2022-05-17 15:14:04.059889+0800 myDemo[252:4951] 这是第 0 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:04.059996+0800 myDemo[252:4951] 这是第 1 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:06.060559+0800 myDemo[252:4951] 这是第 2 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:06.060716+0800 myDemo[252:4951] 这是第 3 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
2022-05-17 15:14:08.061826+0800 myDemo[252:4951] 这是第 4 个任务;线程 <NSThread: 0x282423f80>{number = 1, name = main}
  • 并发队列 + 异步, 同时执行,完成时间不确定,会开启新的线程
//并发队列
dispatch_queue_t comp = dispatch_queue_create("comp", DISPATCH_QUEUE_CONCURRENT);
//异步任务
for (int i=0; i<5; i++) {

    dispatch_async(comp, ^{

        NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
        if (i==1 || i==3) {
            [NSThread sleepForTimeInterval:2];
        }
    });
}
NSLog(@"=============== 所有任务执行完毕 =====");

执行结果:
2022-05-17 15:39:06.309859+0800 myDemo[294:11951] =============== 所有任务执行完毕 =====
2022-05-17 15:39:06.309962+0800 myDemo[294:12131] 这是第 0 个任务;线程 <NSThread: 0x281719380>{number = 5, name = (null)}
2022-05-17 15:39:06.310064+0800 myDemo[294:12131] 这是第 1 个任务;线程 <NSThread: 0x281719380>{number = 5, name = (null)}
2022-05-17 15:39:06.310165+0800 myDemo[294:12134] 这是第 2 个任务;线程 <NSThread: 0x2817d2300>{number = 4, name = (null)}
2022-05-17 15:39:06.310236+0800 myDemo[294:12134] 这是第 3 个任务;线程 <NSThread: 0x2817d2300>{number = 4, name = (null)}
2022-05-17 15:39:06.311765+0800 myDemo[294:12130] 这是第 4 个任务;线程 <NSThread: 0x281719d40>{number = 6, name = (null)}

  • 并发队列 + 同步,顺序执行,先进先出,不会开启线程
//并发队列
dispatch_queue_t comp = dispatch_queue_create("comp", DISPATCH_QUEUE_CONCURRENT);
//同步任务
for (int i=0; i<5; i++) {

    dispatch_sync(comp, ^{

        NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
        if (i==1 || i==3) {
            [NSThread sleepForTimeInterval:2];
        }
    });
}
NSLog(@"=============== 所有任务执行完毕 =====");

执行结果:
2022-05-17 15:37:08.804872+0800 myDemo[287:11040] 这是第 0 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:08.804962+0800 myDemo[287:11040] 这是第 1 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:10.805818+0800 myDemo[287:11040] 这是第 2 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:10.806122+0800 myDemo[287:11040] 这是第 3 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:12.807415+0800 myDemo[287:11040] 这是第 4 个任务;线程 <NSThread: 0x282b3fac0>{number = 1, name = main}
2022-05-17 15:37:12.807771+0800 myDemo[287:11040] =============== 所有任务执行完毕 =====
  • 主队列 + 异步,不会立即执行,需要等待主队列任务执行完成后 才会执行我们添加的异步任务
  • 主队列 + 同步 会相互等待 造成死锁
//同步任务+主线程 == 相互等待,死锁
for (int i=0; i<5; i++) {

        dispatch_sync(dispatch_get_main_queue(), ^{

        NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
        if (i==1 || i==3) {
            [NSThread sleepForTimeInterval:2];
        }
    });
}
  • 面试题
- (void)gcdasy {
    
//    dispatch_queue_t queue = dispatch_queue_create("yiqijiaoyu", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue = dispatch_queue_create("yiqijiaoyu", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        NSLog(@"1");
    });
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_async(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"5");
    });
    
    /// 15234 - 15243,
}
- (void)gcdsy {
    NSLog(@"1");
    dispatch_queue_t queue = dispatch_queue_create("yiqijiaoyu", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"2");
    });
    NSLog(@"3");
    dispatch_sync(queue, ^{
        NSLog(@"4");
    });
    
    /// 1324
}
- (void)test6 {
    
    __block NSInteger a = 0;
    while (a < 10) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            a ++;
            NSLog(@"%ld: %@", a, [NSThread currentThread]);
        });
    }
    
    NSLog(@"=========%ld", a);
    /// a的值不确定 但是肯定大于等于10,a打印的次数不确定,可能很大
}

2、GCD相关的死锁

  • 串行队列 同步线程
- (void)gcdasy {
    
    dispatch_queue_t queue = dispatch_queue_create("yiqijiaoyu", DISPATCH_QUEUE_SERIAL);
//    dispatch_queue_t queue = dispatch_queue_create("yiqijiaoyu", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        NSLog(@"1");
    });
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_sync(queue, ^{// 死锁
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"5");
    });
    
    
}
  • 主队列 + 同步 会相互等待 造成死锁
//同步任务+主线程 == 相互等待,死锁
for (int i=0; i<5; i++) {

        dispatch_sync(dispatch_get_main_queue(), ^{

        NSLog(@"这是第 %d 个任务;线程 %@",i,[NSThread currentThread]);
        if (i==1 || i==3) {
            [NSThread sleepForTimeInterval:2];
        }
    });
}

3、串行并发底层分析

打开libDispatch源码

  • 主队列可在任何地方获取到 ,主队列是一个串行队列,由于可在任何地方获取到 是一个全局的静态变量


    主队列定义

串行的标志性代码是 DQF_WIDTH(1)

  • 初始化队列


    image.png

初始化的时候会判断串行并发标志位去限制width是多少,串行指定是1,并发是14


image.png

来看看_dispatch_queue_init的队列初始化,我们关注的队列是串行和并发的根本区别就是DQF_WIDTH(width),串行是DQF_WIDTH(1)

相关文章

  • GCD的使用

    一、什么是GCD? 在介绍GCD使用之前,我们先来介绍一下什么是GCD,如果我们都不知道GCD是什么 就不用谈GC...

  • 关于益生菌我们应该知道些什么

    这个话题要从肠道的功能说起。 首先肠道是人体最大的消化器官。按中医的说法,胃负责“消”,肠道负责“化”,我...

  • GCD

    GCD 的一些简介 什么是GCD:1.GCD 全称是Grand Central Dispatch, 可以译为“牛逼...

  • GCD 你应该知道的...

    一、定义与实现 GCD 是一套异步执行任务的技术之一,是基于系统级的核心XNU内核级上实现的,所以在iOS 开发中...

  • 关于5G,我们应该知道些什么

    5G已经风风火火的闹的全民皆知了,所以我这个计算机系的人也免不了要凑个热闹~整理了一些5G的资料出来分享一下---...

  • 纯电动车我们应该知道些什么

    纯电动车一定会替代传统燃油车,这是社会和技术进步发展的必然趋势,作为用户和投资者,我们至少应该掌握几点重要信息。 ...

  • 娱乐至死的年代,我们应该关心什么?铭记什么?

    娱乐至死的年代,我们是否该反娱乐了? 我们应该关心些什么?我们应该铭记些什么? 我们应该学习些什么?我们应该鄙弃些...

  • Runtime你应该知道些什么

    Runtime是什么? 字面的意思:运行时,是一套API【用C 和汇编写的】,是iOS的核心之一,是OC的底层,举...

  • 大学,你应该知道些什么

    大学,是比高考还要与你未来更加直接挂钩的阶段,所以,你必须明白一些它的处世之道。 一改变心态,虚心学习 ...

  • GCD详细总结

    一、什么是GCD 二、我们为什么要用GCD技术 三、在实际开发中如何使用GCD更好的实现我们的需求Synchron...

网友评论

    本文标题:GCD我们应该知道些什么

    本文链接:https://www.haomeiwen.com/subject/mnjmzdtx.html