iOS GCD (一)

2018-10-08  本文已影响90人  爬树的蚂蚁

概述

串行队列和并行队列

串行队列 - Serial Dispatch Queue
// 第一个参数为队列的名称;第二个参数填NULL(或者DISPATCH_QUEUE_SERIAL)即为串行队列
dispatch_queue_t serialDispatchQueue = dispatch_queue_create("serialDispatchQueue_0", NULL);
并行队列 - Concurrent Dispatch Queue
// 第二个参数填DISPATCH_QUEUE_CONCURRENT即为并行队列
dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("concurrentDispatchQueue_0", DISPATCH_QUEUE_CONCURRENT);
并行执行的处理的数量问题

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

在这些情况中,只有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函数之使目标队列变成执行阶层的含义了

上一篇下一篇

猜你喜欢

热点阅读