美文网首页
iOS GCD (一)

iOS GCD (一)

作者: 爬树的蚂蚁 | 来源:发表于2018-10-08 11:11 被阅读90次

    概述

    串行队列和并行队列

    串行队列 - Serial Dispatch Queue
    • 一个串行队列只存在一个线程,同时执行的处理数只有一个,并且是按顺序执行;
    • 在多个线程更新相同资源导致数据竞争时使用Serial Dispatch Queue;
    • 串行队列创建
    // 第一个参数为队列的名称;第二个参数填NULL(或者DISPATCH_QUEUE_SERIAL)即为串行队列
    dispatch_queue_t serialDispatchQueue = dispatch_queue_create("serialDispatchQueue_0", NULL);
    
    并行队列 - Concurrent Dispatch Queue
    • 一个并行队列有多个线程,同时执行多个处理,处理之间无顺序;
    • 当想并行执行不发生数据竞争等问题的处理时,使用Concurrent Dispatch Queue
    • 并行队列的创建
    // 第二个参数填DISPATCH_QUEUE_CONCURRENT即为并行队列
    dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("concurrentDispatchQueue_0", DISPATCH_QUEUE_CONCURRENT);
    
    并行执行的处理的数量问题

    并行执行的处理可能出现的情况有多种:

    • 创建多个 Serial Dispatch Queue;
    • 一个Concurrent Dispatch Queue中有多个处理,或者多个Concurrent Dispatch Queue;
    • 同时存在Serial Dispatch Queue和Concurrent Dispatch Queue。

    在这些情况中,只有Concurrent Dispatch Queue中的处理的数量是取决于当前系统的状态的,即iOS和OS X基于 Dispatch Queue 中的处理数、CPU核数以及CPU负荷等当前系统的状态。iOS和OS X的核心 --- XNU内核决定应当使用的线程数,并只生成所需的线程执行处理,另外,当执行的处理数减少时,XNU内核会结束不需要的线程。

    对于其他的情况,处理的数量不受限制,大量生成就会占用资源,消耗大量内存,尤其是生成大量的 Serial Dispatch Queue。

    内存管理

    在iOS6.0(包括6.0)和OS X10.8(包括10.8)之后的版本中,开启ARC的情况下不需要通过dispatch_release()手动管理释放GCD对象;
    而在此版本之前是需要手动管理的,略去不表。

    Dispatch Queue的使用

    通过异步添加进队列的方法dispatch_async()往队列里面添加要执行的任务,如下:

    dispatch_queue_t serialDispatchQueue = dispatch_queue_create("serialDispatchQueue_0", NULL);
    dispatch_async(serialDispatchQueue, ^{
        NSLog(@"串行队列中的任务");
    });
    
    dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("concurrentDispatchQueue_0", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrentDispatchQueue, ^{
        NSLog(@"并行队列中的任务");
    });
    
    同步添加任务和异步添加任务

    异步添加任务的方法:dispatch_async()

    不会等待添加进队列的任务执行,就会执行后面的代码,即异步执行。如下代码:

        dispatch_queue_t serialDispatchQueue = dispatch_queue_create("serialDispatchQueue_0", NULL);
        NSLog(@"1");
        dispatch_async(serialDispatchQueue, ^{
            NSLog(@"串行队列中的任务");
        });
        NSLog(@"2");
    

    结果如下:

    2018-09-30 16:08:47.066070+0800 GCDTest[15737:1710164] 1
    2018-09-30 16:08:47.066279+0800 GCDTest[15737:1710164] 2
    2018-09-30 16:08:47.066334+0800 GCDTest[15737:1710258] 串行队列中的任务
    

    同步添加任务的方法:dispatch_sync()

    会等待添加进队列的任务执行完成,然后再执行后面的代码,会阻塞线程,如下代码:

    dispatch_queue_t serialDispatchQueue = dispatch_queue_create("serialDispatchQueue_0", NULL);
    NSLog(@"1");
    dispatch_sync(serialDispatchQueue, ^{
        NSLog(@"串行队列中的任务");
    });
    NSLog(@"2");
    

    结果如下:

    2018-09-30 16:02:02.730673+0800 GCDTest[15673:1700905] 1
    2018-09-30 16:02:02.730834+0800 GCDTest[15673:1700905] 串行队列中的任务
    2018-09-30 16:02:02.730920+0800 GCDTest[15673:1700905] 2
    

    使用这个函数添加任务,容易导致死锁,尤其是在串行队列,几乎不能使用dispatch_sync函数,看下面代码

        dispatch_queue_t mainqueue = dispatch_get_main_queue();
        dispatch_sync(mainqueue, ^{
            NSLog(@"hellow");
        });
    

    按照代码的意思,主队列执行dispatch_sync函数,如果执行完追加到主队列的blcok就会接着往下走,但是dispatch_sync却要等待主队列执行完dispatch_sync函数的代码,才会执行追加到主队列的block,这样就出现的了相互等待,导致死锁。下面的代码一样会导致死锁

        dispatch_queue_t mainqueue = dispatch_get_main_queue();
        dispatch_async(mainqueue, ^{
            dispatch_sync(mainqueue, ^{
                NSLog(@"hellow");
            });        
        });
    

    串行队列总是按顺序执行,虽然是异步追加,主队列也是要等待block执行完成才会往下执行。

    Main Dispatch Queue和Global Dispatch Queue

    Main Dispatch Queue是在主线程执行的队列,因为主线程只有1个,所以为Serial Dispatch Queue。添加到Main Dispatch Queue中的任务,在主线程的RunLoop中执行,所以更新界面的操作一般追加到Main Dispatch Queue中

        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        dispatch_async(mainQueue, ^{
            NSLog(@"主线程执行任务");
        });
    

    Global Dispatch Queue是所有应用程序都能够使用的Concurrent Dispatch Queue,有四个优先级

    • Height Priority --- DISPATCH_QUEUE_PRIORITY_HIGH
    • Default Priority --- DISPATCH_QUEUE_PRIORITY_DEFAULT
    • Low Priority --- DISPATCH_QUEUE_PRIORITY_LOW
    • Background Priority --- DISPATCH_QUEUE_PRIORITY_BACKGROUND
        // 第二个参数是备用的,填0就可以
        dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(globalQueue, ^{
            NSLog(@"全局队列执行任务");
        });
    

    dispatch_queue_create创建的队列,无论是Serial Dispatch Queue还是Concurrent Dispatch Queue默认优先级均为 DISPATCH_QUEUE_PRIORITY_DEFAULT

    dispatch_set_target_queue

    dispatch_set _target_queue(参数一,参数二 ),参数一是需要变更的队列,参数二是目标队列,最后两个队列的优先级别相同,均为目标队列的优先级。

    dispatch_set_target_queue有两个作用,即变更队列的优先级和使目标队列变为执行阶层

    • 变更队列优先级
        dispatch_queue_t serialQueue_0 = dispatch_queue_create("serialQueue_0", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t targetQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
        
        dispatch_async(serialQueue_0, ^{
            NSLog(@"queue_0");
        });
        dispatch_set_target_queue(serialQueue_0, targetQueue);
    

    这样队列serialQueue_0的优先级与targetQueue队列的优先级相同,但是我觉得并没有什么用,看下面的代码:

        dispatch_queue_t serialQueue_0 = dispatch_queue_create("serialQueue_0", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t serialQueue_1 = dispatch_queue_create("serialQueue_1", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t targetQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    
        dispatch_async(serialQueue_0, ^{
            NSLog(@"queue_0");
        });
        dispatch_async(serialQueue_1, ^{
            NSLog(@"queue_1");
        });
        dispatch_set_target_queue(serialQueue_0, targetQueue);
        
        dispatch_async(serialQueue_0, ^{
            NSLog(@"queue_0");
        });
        dispatch_async(serialQueue_1, ^{
            NSLog(@"queue_1");
        });
    

    按照上面的代码分析,应该队列serialQueue_0在修改优先级之后,其优先级低于serialQueue_1,可是运行结果不是一定的,如下

    2018-10-08 10:40:35.329235+0800 GCDTest[3623:1303883] queue_0
    2018-10-08 10:40:35.329236+0800 GCDTest[3623:1303885] queue_1
    2018-10-08 10:40:35.329406+0800 GCDTest[3623:1303885] queue_1
    2018-10-08 10:40:35.329422+0800 GCDTest[3623:1303883] queue_0
    
    2018-10-08 10:37:27.117724+0800 GCDTest[3595:1298718] queue_1
    2018-10-08 10:37:27.117724+0800 GCDTest[3595:1298715] queue_0
    2018-10-08 10:37:27.117930+0800 GCDTest[3595:1298718] queue_1
    2018-10-08 10:37:27.117934+0800 GCDTest[3595:1298715] queue_0
    

    由于没有一个像样的方法(dipatch_queue_attr_make_with_qos_class函数可以修改,不过不太一样)去指定queue_1的优先级,所以个人觉得,改变优先级的功能少用。

    • 使目标队列变为执行阶层

    如果将多个Serial Dispatch Queue使用dispatch_set_target_queue指定到了同一目标(Serial Dispatch Queue),那么这些串行queue在目标queue上就是同步执行的,不再是并行执行

        dispatch_queue_t serialQueue_0 = dispatch_queue_create("serialQueue_0", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t serialQueue_1 = dispatch_queue_create("serialQueue_1", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t serialQueue_2 = dispatch_queue_create("serialQueue_3", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t targetQueue = dispatch_queue_create("serialQueue_target", DISPATCH_QUEUE_SERIAL);
    
        dispatch_set_target_queue(serialQueue_0, targetQueue);
        dispatch_set_target_queue(serialQueue_1, targetQueue);
        dispatch_set_target_queue(serialQueue_2, targetQueue);
        
        dispatch_async(serialQueue_0, ^{
            NSLog(@"queue_0");
            [NSThread sleepForTimeInterval:2.f];
            NSLog(@"queue_0 sleep");
        });
        dispatch_async(serialQueue_1, ^{
            NSLog(@"queue_1");
            [NSThread sleepForTimeInterval:2.f];
            NSLog(@"queue_1 sleep");
        });
        dispatch_async(serialQueue_2, ^{
            NSLog(@"queue_2");
            [NSThread sleepForTimeInterval:2.f];
            NSLog(@"queue_2 sleep");
        });
    

    执行结果如下:

    2018-10-08 10:58:11.531291+0800 GCDTest[3844:1332951] queue_0
    2018-10-08 10:58:13.531850+0800 GCDTest[3844:1332951] queue_0 sleep
    2018-10-08 10:58:13.532047+0800 GCDTest[3844:1332951] queue_1
    2018-10-08 10:58:15.533480+0800 GCDTest[3844:1332951] queue_1 sleep
    2018-10-08 10:58:15.533886+0800 GCDTest[3844:1332951] queue_2
    2018-10-08 10:58:17.538822+0800 GCDTest[3844:1332951] queue_2 sleep
    

    修改下代码,注释掉如下代码

    //    dispatch_set_target_queue(serialQueue_0, targetQueue);
    //    dispatch_set_target_queue(serialQueue_1, targetQueue);
    //    dispatch_set_target_queue(serialQueue_2, targetQueue);
    

    运行结果如下:

    2018-10-08 11:03:23.287384+0800 GCDTest[3899:1341109] queue_1
    2018-10-08 11:03:23.287384+0800 GCDTest[3899:1341112] queue_2
    2018-10-08 11:03:23.287420+0800 GCDTest[3899:1341110] queue_0
    2018-10-08 11:03:25.292805+0800 GCDTest[3899:1341110] queue_0 sleep
    2018-10-08 11:03:25.292798+0800 GCDTest[3899:1341112] queue_2 sleep
    2018-10-08 11:03:25.292798+0800 GCDTest[3899:1341109] queue_1 sleep
    

    这样就明白,dispatch_set_target_queue函数之使目标队列变成执行阶层的含义了

    相关文章

      网友评论

          本文标题:iOS GCD (一)

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