美文网首页@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我们应该知道些什么

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