iOS GCD (一)
概述
略
串行队列和并行队列
串行队列 - 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函数之使目标队列变成执行阶层的含义了