1、添加任务到队列(同步、异步):
dispatch_sync:
同步添加,将指定的任务block同步追加到queue中,在追加的block结束之前,dispatch_sync会一直等待;
如果在主线程上执行同步任务(任务也在主线程执行),会造成死锁:
代码语言:javascript复制//会造成死锁
dispatch_sync(dispatch_get_main_queue(), ^{
//
});
但是在异步任务里可以用来切换到主线程
代码语言:javascript复制dispatch_async(dispatch_get_global_queue(0,0), ^{
//耗时操作;
dispatch_sync(dispatch_get_main_queue(), ^{
//回到主线程;
});
});
可以用来阻塞线程:
代码语言:javascript复制dispatch_sync(dispatch_get_global_queue(0,0), ^{
sleep(2);
NSLog(@"2秒后执行,但是会阻塞线程");
});
代码语言:javascript复制dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2秒后执行,但是不阻塞线程");
});
dispatch_async:
异步添加,将指定的Block”非同步”地追加到指定的Queue中,该线程不做等待,继续往下执行;
2、队列
Dispatch Queue:执行处理任务的队列,通过dispatch_async等函数API,在Block语法中记述想要执行的处理任务并将其追加到Dispatch Queue中,Dispatch Queue按照追加的顺序(先进先出FIFO)执行处理。
Serial Dispatch Queue(串行队列):
等待现在执行中处理结束,一旦生成Serial Dispatch Queue并追加处理。系统对于一个Serial Dispatch Queue就只生成并使用一个线程,但是如果生成过多的线程,会导致消耗大量的内存,引起大量的上下文切换,大幅度降低系统的响应性能,因此只在为了避免多个线程更新相同的资源导致数据竞争时使用。
Concurrent Dispatch Queue(并行队列):
不等待现在执行中处理结束,可以并行执行多个处理,并行处理的处理数量取决于当前系统状态,生成所需的线程执行处理,当处理结束,应当执行的处理数减少时,XNU内核会结束不再需要的线程,因此当想并行执行不发生数据竞争等问题处理时使用并行队列,有效管理线程,不会出现太多线程。
系统默认有一个串行队列:主队列(main_queue)和并行队列:全局队列(global_queue),不需要自己手动释放,或者自己创建用户队列(需要手动释放)。
主队列(main):
dispatch_queue_t mainQ = dispatch_get_main_queue() 注意:不能sync向主队列提交任务,因为会造成死锁,只能async提交任务
全局队列(global_queue):并发队列
有执行优先级
代码语言:javascript复制dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
DISPATCH_QUEUE_PRIORITY_HIGH: //优先级最高,在default,和low之前执行
DISPATCH_QUEUE_PRIORITY_DEFAULT //默认优先级,在low之前,在high之后
DISPATCH_QUEUE_PRIORITY_LOW //在high和default后执行
DISPATCH_QUEUE_PRIORITY_BACKGROUND //提交到这个队列的任务会在high优先级的任务和已经提交到background队列的执行完后执行。
获取队列:(create_queue):
如果是串行队列:dispatch_queue_create(“createName”, DISPATCH_QUEUE_SERIAL)
如果是并行队列:推荐使用dispatch_get_global_queue
如果需要用到barrier和group,才来dispatch_queue_create(“createName”,DISPATCH_QUEUE_CONCURRENT)
尽管是ARC,使用结束后也要dispatch_release释放
代码语言:javascript复制dispatch_queue_t concurrentQ = dispatch_queue_create("createName",DISPATCH_QUEUE_CONCURRENT)
dispatch_queue_t serialQ = dispatch_queue_create("createName", DISPATCH_QUEUE_SERIAL)
改变队列优先级
dispatch_set_target_queue(restQueue, targetQueue);
暂停恢复队列
代码语言:javascript复制dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_suspend(queue); //暂停队列queue
dispatch_resume(queue); //恢复队列queue
3、延迟添加执行dispatch_after
并不是在多少秒后执行,而是在3秒后将任务添加到Dispatch Queue中
代码语言:javascript复制dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3*NSEC_PER_SEC)),dispatch_get_main_queue(), ^{
//秒
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,150ull *NSEC_PER_MSEC),dispatch_get_main_queue(), ^{
//毫秒
});
4、dispatch_group与dispatch_barrier
dispatch_group应用于线程依赖:当添加到Queue中的所有任务都完成了,再来执行某个任务。
举例,异步下载两张图片,这在这两张图片下载完毕之后,将两张图片合成
代码语言:javascript复制dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.gcd-group.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
#异步下载第一张图片
});
dispatch_group_async(group, queue, ^{
#异步下载第二张图片
});
dispatch_group_notify(group, queue, ^{
#两张图片下载完后进行合成
});
dispatch_barrier:barrier以为栅栏,阻碍。在串行队列中,无区别,但是在并行队列中,用这个函数添加的任务可以保证阻碍线程执行,即这个任务是串行执行的。可用于解决数据竞争问题。有个坑要注意,并行队列必必须是自己dispatch_queue_create创建的,不能用dispatch_get_global_queue
实现原理:queue中的其他block已经开始在各自分配的线程执行,当从queue检出一个barrier时,是等待其他的block都执行完毕再执行barrier,此时不会并发执行其他的block,直到该barrier执行完毕。 这相当于给当前已经运行的bock们加了一个group和notify,在notify里面执行了barrier,然后再执行barrier下面的block
举例。多个操作对数据库进行读写,读的操作可以异步进行,但是为了保证写的线程安全,则必须用dispatch_barrier
代码语言:javascript复制dispatch_async(queue, ^{NSLog(@"read");});
dispatch_async(queue, ^{NSLog(@"read");});
dispatch_async(queue, ^{NSLog(@"read");});
#可以保证写的时候只有这一个操作
dispatch_barrier_sync(queue, ^{NSLog(@"writing");});
dispatch_async(queue, ^{NSLog(@"read");});
dispatch_async(queue, ^{NSLog(@"read");});
5、dispatch_semaphore
semaphore是信号的意思,dispatch_semaphore有三个函数,分别是dispatch_semaphore_create,dispatch_semaphore_signal,dispatch_semaphore_wait
dispatch_semaphore信号量控制
6、dispatch_apply
重复执行某段代码
在某些场景下使用dispatch_apply会对性能有很大的提升,比如你的代码需要以每个像素为基准来处理计算image图片。同时dispatch apply能够避免一些线程爆炸的情况发生
代码语言:javascript复制//危险,可能导致线程爆炸以及死锁
for (int i = 0; i < 999; i ){
dispatch_async(q, ^{...});
}
dispatch_barrier_sync(q, ^{});
// 较优选择, GCD 会管理并发
dispatch_apply(999, q, ^(size_t i){...});
7、dispatch_block_cancel
iOS8之后,提交到gcd队列中的dispatch block也可取消了,只需要简单的调用dispatch_block_cancel传入想要取消的block即可:
代码语言:javascript复制dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block1 = dispatch_block_create(0, ^{
NSLog(@"block1 begin");
[NSThread sleepForTimeInterval:1];
NSLog(@"block1 done");
});
dispatch_block_t block2 = dispatch_block_create(0, ^{
NSLog(@"block2 ");
});
dispatch_async(queue, block1);
dispatch_async(queue, block2);
dispatch_block_cancel(block2);