美文网首页iOS-多线程
11--多线程02--GCD初探

11--多线程02--GCD初探

作者: 修_远 | 来源:发表于2021-07-04 12:39 被阅读0次

    [TOC]

    (一)GCD 介绍

    1.1 GCD简介

    • 什么是GCD?
      • 全称是 Grand Central Dispatch
      • 纯 C 语言,提供了非常多强大的函数
    • GCD的优势
      • GCD 是苹果公司为多核的并行运行提出的解决方案
      • GCD 会自动利用更多的 CPU 内核(比如双核、四核)
      • GCD 会自动管理线程的声明周期(创建线程、调度任务、销毁线程)
      • 程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码

    1.2 函数

    将任务添加到队列,并且制定执行任务的函数

    • 异步 dispatch_async

      • 任务使用 block 封装
      • 任务的 block 没有参数也没有返回值
      • 执行任务的函数
      • 不用等待当前语句执行完毕,就可以执行下一条语句
      • 会开启线程执行 block 的任务
      • 异步是多线程的代名词
    • 同步 dispatch_sync

      • 必须等待当前语句执行完毕,才会执行下一条语句
      • 不会开启线程
      • 在当前执行 block 的任务

    1.3 队列

    队列
    • 队列是一种数据结构,具有先进先出(FIFO)的特性
    • 串行队列是按顺序执行任务的
    • 并发队列中任务执行的数据不确定
      • 有可能任务4最先执行完,因为它最简单,异步任务的执行速度跟任务的复杂度有关

    iOS系统提供了主队列和全局队列

    • 主队列

      • dispatch_get_main_queue()
      • 如果当前主线程正在有任务执行,那么无论主队列中当前被添加了什么任务,都不会被调度
      • 专门用来在主线程上调度任务的队列
      • 不会开启线程
      • UI为什么要在主线程上刷新?
        1. 安全+效率:UIKit不是线程安全的,当多个线程同时操作UI时,资源抢夺导致崩溃,UI异常等问题;
        2. 用户体验:子线程中一般都会运行很多耗时的后台任务,有可能会影响UI的刷新;
    • 全局队列

      • dispatch_get_global_queue(0, 0)
      • 全局队列是一个并发队列
      • 为了方便程序员的使用,苹果提供了全局队列
      • 在使用多线程并发时,如果对队列没有特殊需求,在执行异步任务时,可以直接使用全局队列

    1.4 队列和函数

    串行队列 并发队列
    同步函数
    异步函数

    1.5 死锁

    • 主线程的死锁

      • 主线程因为同步函数的原因等着先执行任务
      • 主队列等着主线程的任务执行完毕再执行自己的任务
      • 主队列和主线程相互等待会造成死锁
    • 死锁的条件

      • 互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

      • 请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

      • 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。

      • 循环等待条件:若干进程间形成首尾相接循环等待资源的关系

    (二)GCD的应用

    2.1 dispatch_async 和 dispatch_sync

    测试代码1

    dispatch_queue_t queue = dispatch_queue_create("xy", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
    
    • 输出结果1:1(52)34
    • 1,在当前线程,也就是主线程中执行,肯定是第一个;
    • (52),虽然5也是主线程中,理论上要在2前面执行,但当任务非常简单时,2有可能会在5的前面执行;
    • (34),3是同步执行,肯定要在4前面;
    2020-07-07 23:47:10.420291+0800 001---函数与队列[3440:96357] 1
    2020-07-07 23:47:10.421551+0800 001---函数与队列[3440:96357] 5
    2020-07-07 23:47:10.421625+0800 001---函数与队列[3440:96598] 2
    2020-07-07 23:47:10.421798+0800 001---函数与队列[3440:96598] 3
    2020-07-07 23:47:10.421930+0800 001---函数与队列[3440:96598] 4
    

    测试代码2

    // 同步队列
    dispatch_queue_t queue = dispatch_queue_create("xy", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1");
    // 异步函数
    dispatch_async(queue, ^{
        NSLog(@"2");
        NSLog(@"4");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
    });
    NSLog(@"5");
    
    • 输出结果1524 crash
    • 因为 queue 是一个串行队列,同步等待一个串行任务是会发生死锁的
    • 有一个面试的程序员解释了这个现象。面试官问:“什么是死锁”,程序员说:“你让我进公司,我就告诉你什么是死锁”


    测试代码3

    dispatch_queue_t queue = dispatch_queue_create("com.xy.cn", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"1");
    });
    dispatch_async(queue, ^{
        NSLog(@"2");
    });
    dispatch_sync(queue, ^{
        NSLog(@"3");
    });
    
    NSLog(@"0");
    
    dispatch_async(queue, ^{
        NSLog(@"7");
    });
    dispatch_async(queue, ^{
        NSLog(@"8");
    });
    dispatch_async(queue, ^{
        NSLog(@"9");
    });
    
    • 输出结果:(312) 0 (789),
      • 312 在 0 的前面,因为第一个异步任务太短,有可能在第三个同步任务添加之前就已经完成,所以 123 顺序不确定;
      • 0 一定在 3 后面,因为任务三是一个同步任务,必须要等任务三执行完成之后,才会执行后面的任务;
      • 0 是在当前线程的任务,就没啥好说的
      • 789 都是异步任务,顺序无法确定

    相关文章

      网友评论

        本文标题:11--多线程02--GCD初探

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