美文网首页
结合GCD简单介绍下同步、异步、并行、串行、线程、多线程等概念

结合GCD简单介绍下同步、异步、并行、串行、线程、多线程等概念

作者: Miridescent | 来源:发表于2018-05-23 14:51 被阅读40次

    一、简单问答环节

    1.什么是GCD

    答:一种异步执行任务的技术

    2.什么是线程

    答:源代码通过编译器转换为CPU命令列,即二进制代码,一个CPU执行这些二进制代码的形式为一行一行执行,执行顺序是线性的(永远不会出现分叉),即为线程

    3.什么是多线程

    答:一个CPU执行多条这种线性的无分叉路径,即为多线程

    4.一个CPU如何实现多线程

    答:一个CPU同时只能做一件事,当CPU在多个线程间频繁的切换,由于速度很快,给人的感觉就是同时执行多个线程

    5.多线程会产生哪些问题

    答:1.更新相同的资源,导致数据不一致(数据竞争)
    2.多个线程之间相互等待,谁也不往下执行(死锁)
    3.多线程对内存消耗很大
    还有很多问题,不一一列举

    二、结合GCD的API讲解

    1.Dispatch Queue(调度队列)

    a.队列的种类

    队列可以通俗的理解为坐火车检票,乘客排着队准备通过检票口,而每一个乘客相当于一个要执行的代码块(任务)
    队列总体分为两种类型 Serial Dispatch QueueConcurrent Dispatch Queue,可以通俗的理解为一个检票口,和多个检票口(即串行队列和并行队列)
    Serial Dispatch Queue 类型的队列每次只允许一个乘客通过,后面的乘客想通过检票口,必须要等前面的乘客先通过(每次只能执行一个代码块)
    Concurrent Dispatch Queue 类型的队列相当于有多个检票口,可以同时允许很多乘客检票(即同时执行很多个代码块)

    b.创建一个队列

    队列的类型是dispatch_queue_t创建一个队列用dispatch_queue_create方法

    // 创建一个Serial类型的队列
    dispatch_queue_t   serial_queue = dispatch_queue_create("com.ms.serial", NULL);
    
    // 创建一个concurrent类型的队列
        dispatch_queue_t     concurrent_queue = dispatch_queue_create("com.ms.concurrent", DISPATCH_QUEUE_CONCURRENT);
    

    第一个参数是队列的名字,一般用程序ID逆序全域名的形式,这个参数在程序报错时候的返回信息中会体现,还是很有用的
    第二个参数为生成指定类型队列的参数

    注意

    • 每生成一个Serial类型的队列相当于生成并使用一个线程,所以不宜生成过多的Serial类型队列,这样内存会开销很大
    • 一个线程对数据来说是安全的,所以可以针对一个数据生成一个Serial类型的队列,例如更新一个文件,但是要避免针对一个文件生成多个Serial类型的队列,因为多线程的关系,会产生数据竞争
    • 当想要对一个数据进行并发操作,又要避免数据竞争问题的时候,用Concurrent类型的队列,系统会有效的管理这些线程,避免出现问题

    c.系统自带的队列

    GCD的API为我们准备了两个队列Main Dispatch QueueGlobal Dispatch Queue分别对应Serial类型的队列和Concurrent类型的队列

    // 获取Main Dispatch Queue
    dispatch_queue_t main_queue = dispatch_get_main_queue();
    // 获取Global Dispatch Queue
    dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    

    Global Dispatch Queue有四种执行优先级,高优先级相当于会员的作用,可以让你插队,对应dispatch_get_global_queue方法的第一个参数,分别是

    参数 优先级
    DISPATCH_QUEUE_PRIORITY_HIGH 高优先级
    DISPATCH_QUEUE_PRIORITY_DEFAULT 默认优先级
    DISPATCH_QUEUE_PRIORITY_LOW 低优先级
    DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台优先级

    需要注意,优先级只是大致的判断,并不是绝对的

    d.修改队列优先级

    dispatch_queue_create方法创建的队列优先级与Global Dispatch Queue的默认优先级相同,即DISPATCH_QUEUE_PRIORITY_DEFAULT
    想变更生成的队列的优先级的时候,用方法dispatch_set_target_queue
    例子是将创建的concurrent_queue的优先级设置成DISPATCH_QUEUE_PRIORITY_BACKGROUND优先级

    // 创建的队列,优先级是DISPATCH_QUEUE_PRIORITY_DEFAULT
    dispatch_queue_t concurrent_queue = dispatch_queue_create("com.ms.concurrent", DISPATCH_QUEUE_CONCURRENT);
    // 获取的Global Dispatch Queue对列,注意优先级是 DISPATCH_QUEUE_PRIORITY_BACKGROUND
    dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    // 修改concurrent_queue的优先级
    dispatch_set_target_queue(concurrent_queue, global_queue);
    

    注意

    • 不可以修改Main Dispatch Queue 和 Global Dispatch Queue的优先级

    dispatch_set_target_queue的另一个作用是可以决定队列的执行层次,防止多个队列并行执行
    假设有五个Serial Dispatch Queue,那么这五个Serial Dispatch Queue是并行执行的,如果将其中四个Serial Dispatch Queue设置为其中某一个Serial Dispatch Queue的优先级,则不会出现并行执行的情况

    2.添加任务到队列

    前面已经讲解了如何创建一个队列,下面介绍如何将任务添加到队列中
    将任务添加到队列分为同步(sync)异步(async)
    举个例子,你有车了,为了嘚瑟有车,要开车去送你的朋友去火车站坐火车,但是你可能有好几个朋友要去坐火车,但是这几个朋友之间相互不认识,分别在不同的地方,要分别送他们
    同步(sync)就是你送一个朋友去火车站之后,必须确认你朋友经过检票口上了火车之后你才回去接其他的朋友,也不管其他的朋友是否着急
    异步(async)就是你送去一个朋友之后,也没管它是否检票上车,就返回去接其他的朋友了

    a. dispatch_sync()

    void  dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
    

    将任务同步添加到队列中
    例子是将打印1的任务同步添加到serial_queue队列中

    dispatch_queue_t serial_queue = dispatch_queue_create("com.ms.serial", NULL);
    dispatch_sync(serial_queue, ^{
            // 要做的任务
            NSLog(@"1");
        });
    
    • 第一个参数是要添加的队列
    • 第二个参数是block,就是我们要添加的任务

    b. dispatch_sync_f()

    void  dispatch_sync_f(dispatch_queue_t queue, void *_Nullable context, dispatch_function_t work);
    

    作用和dispatch_sync()方法一样,不同点是这个方法可以将函数提交到队列中
    例子中是将一个函数print1()添加到队列中,实现的也是打印1的任务

    void print1(void *param){
        printf("%s", param);
    }
    char *str = "1";
    dispatch_sync_f(serial_queue, str, print1);
    
    • 第一个参数是要添加的队列
    • 第二个参数是地址,这个参数是第三个参数(一个函数)要用的参数
    • 第三个参数是一个void (*dispatch_function_t)(void *_Nullable)类型的函数

    c. dispatch_async()和dispatch_async_f()

    类比上面的ab,就是同步和异步的区别

    将任务添加到队列后,系统会对队列进行retain,当任务完成后队列被release

    注意

    • dispatch_sync()和dispatch_async()中第二个参数是block,这个block中即使使用self也不会出现循环引用的情况,可以放心使用
    • dispatch_sync()别乱用,在串行队列中用dispatch_sync()将任务添加到当前串行队列,会发生死锁,因为block的执行需要等待前面的任务,也就是dispatch_sync执行完成,两者互相等待,在并行队列中就不会出现这种情况
    • 前面介绍过Main Dispatch Queue是串行队列,你懂的😏

    相关文章

      网友评论

          本文标题:结合GCD简单介绍下同步、异步、并行、串行、线程、多线程等概念

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