本文主要介绍GCD一些API的使用,也就是注重实站!
一、GCD常用API
- 1.并发队列
- 2.串行队列
- 3.设置自定义队列的优先级(dispatch_set_target_queue)
- 4.线程组(dispatch_group)
- 5.栅栏操作(dispatch_barrier_async)
- 6.队列的暂停(dispatch_suspend)与重启( dispatch_resume)
- 7.多少时间后执行任务(dispatch_after)
- 8.一次执行(dispatch_once)
二、GCD一些辅助API
- 1.dispatch_group_enter(group)、dispatch_group_leave(group)
- 2.线程组等待(dispatch_group_wait)
- 3.信号量(dispatch_semaphore_t)
- 4.快速迭代(dispatch_apply)
一、GCD常用API
1.并发队列(Concurrent Dispatch Queue)
只要有空闲的线程,队列就会调度当前任务,交给线程去执行,不需要考虑前面是都有任务在执行,只要有线程可以利用,队列就会调度任务。
//创建自定义并行队列
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
//创建全局队列
dispatch_queue_t globQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
并发功能只有在异步(dispatch_async)函数下才有效,在同步函数下失去了任务调度的并发能力.
2.串行队列(Serial Dispatch Queue):
任务按照顺序被调度,前一个任务不执行完毕,队列不会调度,这样任务,都是一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
// 串行队列的创建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// 使用主队列(跟主线程相关联的队列)
// 主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行
dispatch_queue_t queue = dispatch_get_main_queue();
3.设置队列的优先级(dispatch_set_target_queue)
1)设置系统提供并行队列的优先级
第一个参数为要设置优先级的queue,第二个参数是参照物,既将第一个queue的优先级和第二个queue的优先级设置一样。
/* NSLog(@"执行系统提供的并行队列,这个队列有优先级,可以手动设置");
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
*/
dispatch_queue_t global_queue_background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(global_queue_background, ^{
NSLog(@"我是第四:DISPATCH_QUEUE_PRIORITY_BACKGROUND");
});
dispatch_queue_t global_queue_low = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_async(global_queue_low, ^{
NSLog(@"我是第三:DISPATCH_QUEUE_PRIORITY_LOW");
});
dispatch_queue_t global_queue_default = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(global_queue_default, ^{
NSLog(@"我是第二:DISPATCH_QUEUE_PRIORITY_DEFAULT");
});
dispatch_queue_t global_queue_High = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(global_queue_High, ^{
NSLog(@"我是第一:DISPATCH_QUEUE_PRIORITY_HIGH");
});
2)设置自定义队列的优先级(dispatch_set_target_queue)
dispatch_queue_t concurrentQueueT1 = dispatch_queue_create("com.tiankong.GCDDemo", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t global_queue_low = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_set_target_queue(concurrentQueueT1, global_queue_low);
dispatch_async(concurrentQueueT1, ^{
NSLog(@"concurrentQueueT1我设置了最低优先级");
});
dispatch_queue_t concurrentQueueT2 = dispatch_queue_create("com.tiankong.GCDDemo", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t global_queue_High = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_set_target_queue(concurrentQueueT2, global_queue_High);
dispatch_async(concurrentQueueT2, ^{
NSLog(@"concurrentQueueT2最先执行,因为把我的优先级设为了最高");
});
4.线程组(dispatch_group)
在追加到Dispatch Queue中的多个任务处理完毕之后想执行结束处理,这种需求会经常出现。如果只是使用一个Serial Dispatch Queue(串行队列)时,只要将想执行的处理全部追加到该串行队列中并在最后追加结束处理即可,但是在使用Concurrent Queue 时,可能会同时使用多个Dispatch Queue时,源代码就会变得很复杂。
在这种情况下,就可以使用dispatch_group。
/*
关灯,哥哥学习10分钟、姐姐学习20分钟、我学习15分钟,最后关灯睡觉(下面的例子1s = 1m)
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group_t = dispatch_group_create();
dispatch_group_async(group_t, queue, ^{
//任务执行
sleep(10);
NSLog(@"哥哥学习10分钟");
});
dispatch_group_async(group_t, queue, ^{
sleep(20);
NSLog(@"姐姐学习20分钟");
});
dispatch_group_async(group_t, queue, ^{
sleep(15);
NSLog(@"我学习15分钟");
});
dispatch_group_notify(group_t, queue, ^{
//上面的线程全部完成
NSLog(@"关灯睡觉");
});
5.栅栏操作(dispatch_barrier_async)
- 只适于自己创建的队列dispatch_queue_create的队列
- 并行队列实现高效的数据访问和文件访问
__block int a = 5;
dispatch_queue_t concurrentQueueT = dispatch_queue_create("com.tiankong.GCDDemo", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueueT, ^{
NSLog(@"读取第一次a == %d",a);
});
dispatch_async(concurrentQueueT, ^{
NSLog(@"读取第二次a == %d",a);
});
dispatch_async(concurrentQueueT, ^{
NSLog(@"读取第三次a == %d",a);
});
// dispatch_async(concurrentQueueT, ^{//这么写,读取的值不能确定,
// a = 8;
// NSLog(@"写入操作a = 8");
// });
dispatch_barrier_async(concurrentQueueT, ^{
a = 8;
NSLog(@"写入a = 8");
});
dispatch_async(concurrentQueueT, ^{
NSLog(@"读取第四次a == %d",a);
});
dispatch_async(concurrentQueueT, ^{
NSLog(@"读取第五次a == %d",a);
});
dispatch_async(concurrentQueueT, ^{
NSLog(@"读取第六次a == %d",a);
});
6.队列的暂停(dispatch_suspend)与重启( dispatch_resume)
- 注意:必须成对出现
dispatch_queue_t concurrentQueueT = dispatch_queue_create("com.tiankong.GCDDemo", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueueT, ^{
NSLog(@"读取第一次");
});
dispatch_suspend(concurrentQueueT);
sleep(10);
dispatch_resume(concurrentQueueT);
dispatch_async(concurrentQueueT, ^{
NSLog(@"读取第二次");
});
7.多少时间后执行任务(dispatch_after)
下面这句dispatch_after的真正含义是在2秒后把任务添加进队列中,并不是表示在2秒后执行,大部分情况该函数能达到我们的预期,只有在对时间要求非常精准的情况下才可能会出现问题。
- 注意dispatch_after(每10秒会有1秒的误差,10秒面变成11秒,实践所得)
延迟2秒执行
NSTimeInterval duration = 2.0;
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)((duration)/*延迟执行时间*/ * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), ^{
//执行2秒后的操作
});
8.一次执行(dispatch_once)
//一次性执行
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
// some one-time task
});
二、GCD一些辅助API
1.dispatch_group_enter(group)、dispatch_group_leave(group)
个人理解:和内存管理的引用计数类似,我们可以认为group也持有一个整形变量(只是假设),当调用enter时计数加1,调用leave时计数减1,当计数为0时会调用dispatch_group_notify并且dispatch_group_wait会停止等待;
- 下面的例子你可以去掉dispatch_group_enter(group)、dispatch_group_leave(group)试试,这种情况就是项目中UI需要多个接口返回数据展示的方法
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group_t = dispatch_group_create();
dispatch_group_enter(group_t);
dispatch_group_async(group_t, queue, ^{
dispatch_async(queue1, ^{
sleep(3);
NSLog(@"我学习15分钟");
dispatch_group_leave(group_t);
});
});
dispatch_group_notify(group_t, queue, ^{
//上面的线程全部完成
NSLog(@"关灯睡觉");
});
2.线程组等待(dispatch_group_wait)
个人理解:和dispatch_group_notify功能类似(多了一个dispatch_time_t参数可以设置超时时间),在group上任务完成前,dispatch_group_wait会阻塞当前线程(所以不能放在主线程调用)一直等待;当group上任务完成,或者等待时间超过设置的超时时间会结束等待;
/*
关灯,哥哥学习10分钟、姐姐学习20分钟、我学习15分钟,最后关灯睡觉(下面的例子1s = 1m)
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group_t = dispatch_group_create();
dispatch_group_async(group_t, queue, ^{
//任务执行
sleep(10);
NSLog(@"哥哥学习10分钟");
});
dispatch_group_async(group_t, queue, ^{
sleep(20);
NSLog(@"姐姐学习20分钟");
});
dispatch_group_async(group_t, queue, ^{
sleep(15);
NSLog(@"我学习15分钟");
});
dispatch_group_notify(group_t, queue, ^{
//上面的线程全部完成
NSLog(@"关灯睡觉");
});
dispatch_async(queue, ^{
dispatch_group_wait(group_t, dispatch_time(DISPATCH_TIME_NOW, 16 * NSEC_PER_SEC));
NSLog(@"dispatch_group_wait 结束");
});
3.信号量(dispatch_semaphore_t)
停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此时来了五辆车,那么就有一辆需要等待。
信号量的值就相当于剩余车位的数目,dispatch_semaphore_wait函数就相当于来了一辆车,dispatch_semaphore_signal
就相当于走了一辆车。停车位的剩余数目在初始化的时候就已经指明了(dispatch_semaphore_create(long value)),
调用一次dispatch_semaphore_signal,剩余的车位就增加一个;调用一次dispatch_semaphore_wait剩余车位就减少一个;
当剩余车位为0时,再来车(即调用dispatch_semaphore_wait)就只能等待。有可能同时有几辆车等待一个停车位。有些车主
没有耐心,给自己设定了一段等待时间,这段时间内等不到停车位就走了,如果等到了就开进去停车。而有些车主就像把车停在这,
所以就一直等下去
-(void)semaphore
{
NSMutableArray *array = [[NSMutableArray alloc] init];
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
for (int i=0; i<10000; i++) {
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[array addObject:[NSNumber numberWithInt:i]];
NSLog(@"%@\n",[NSThread currentThread]
);
dispatch_semaphore_signal(sem);
});
}
}
4.快速迭代(dispatch_apply)
要比for循环快,for 循环是在主线程运行次数大一定会造成卡顿,dispatch_apply异步的并发的队列,自动开启子线程,因此dispatch_apply要快一些
dispatch_apply(10,dispatch_get_global_queue(0, 0), ^(size_t index) {
NSLog(@"%zd = %@",index,[NSThread currentThread]);
});
//打印结果
2017-02-16 15:10:38.602 02-掌握-GCD的快速迭代-zwj[1447:102911] 3 = <NSThread: 0x608000268e40>{number = 5, name = (null)}
2017-02-16 15:10:38.602 02-掌握-GCD的快速迭代-zwj[1447:102666] 0 = <NSThread: 0x60000007ee80>{number = 1, name = main}
2017-02-16 15:10:38.602 02-掌握-GCD的快速迭代-zwj[1447:102912] 2 = <NSThread: 0x60000026ca00>{number = 4, name = (null)}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102910] 1 = <NSThread: 0x60800007eac0>{number = 3, name = (null)}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102911] 4 = <NSThread: 0x608000268e40>{number = 5, name = (null)}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102666] 5 = <NSThread: 0x60000007ee80>{number = 1, name = main}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102912] 6 = <NSThread: 0x60000026ca00>{number = 4, name = (null)}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102910] 7 = <NSThread: 0x60800007eac0>{number = 3, name = (null)}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102911] 8 = <NSThread: 0x608000268e40>{number = 5, name = (null)}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102666] 9 = <NSThread: 0x60000007ee80>{number = 1, name = main}
最后附上GCDDemo
如果有什么GCD的问题,欢迎留言;如果你对gcd不太熟悉,那么可以点收藏或者关注我哦,方便将来复制操作。