iOS与OS X多线程和内存管理 - GCD
前言
iOS与OS X多线程和内存管理 读书笔记(三) GCD
笔记
GCD概要
- GCD:Grand Central Dispatch,大中枢派发,很牛逼的名字
GCD的API
dispatch_async(queue, block);
将想要执行的任务封装进Block,放他到queue中执行。这里的queue有两种:
- 一种是Serial Dispatch Queue,顺序队列,放入这里面的block任务是一个一个执行的,先进先出。
- 另一种是 Concurrent Dispatch Queue,并行队列,意思就是会开多个队列,block一次进入各个队列同时执行;但是队列的数量由系统控制,那些还来不及进入任何一个队列执行的block,只能等某个队列的block执行完了,再进入队列执行了;所以说这里的每个block的执行顺序是不确定的。
PS:当然你也可以同时开多个顺序队列,以达到并行队列的效果;但是不能多开,因为程序在各个队列之间切换,也就是在各个线程之间切换,也就是所谓的上下文切换,需要耗费CPU等资源。当然存放每个线程的状态等信息也需要耗费资源。
dispatch_queue_create;
这个API用来生成Dispatch Queue。看看下面代码:
1
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.vanney9.serialQueue", NULL);
上面的代码用来生成一个Serial dispatch queue,这个函数有两个参数:第一个是queue的名字,最好唯一;第二个是代表生成的queue的类型,若指定为NULL,则生成serial dispatch queue,若指定为
DISPATCH_QUEUE_CONCURRENT
,则生成concurrent dispatch queue。返回类型是dispatch_queue_t。PS:生成的queue,要通过
dispatch_release
函数手动释放。dispatch_get_main_queue();
这个函数用来获取程序的主线程,主线程只有一个,当然是serial dispatch queue。追加到主线程执行的任务会在主线程的Runloop中执行。
dispatch_get_global_queue(level, 0);
这个函数用来获取Global dispatch queue。它是一个Concurrent dispatch queue。它有4个人优先级:High Priority,Default Priority,Low Priority和Background Priority。
dispatch_set_target_queue
(有点晕)该函数用来设置queue的优先级。默认情况下,使用
dispatch_queue_create
生成的queue都是和Global dispatch queue的默认优先级相同。可以使用该函数来改变优先级:1
2
3dispatch_queue_t mySerialQueue = dispatch_queue_create("com.vanney9.serialQueue", NULL); // 优先级为Default Priority
dispatch_queue_t globalBackgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); // 获取Background Priority的queue
dispatch_set_target_queue(mySerialQueue, globalBackgroundQueue); // 设置mySerialQueue的优先级和globalBackgroundQueue的相同dispatch_after(time, queue, block);
该函数表示在time时间之后(比如3s后)将block加到queue中。
Dispatch Group
追加到dispatch queue中的多个处理都结束时,想进行一些处理该如何实现呢?对于serial dispatch queue,这很简单;但是如果是concurrent dispatch queue,这时候就需要Dispatch Group。如下:
1
2
3
4
5
6
7
8
9
10
11dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //先创建一个concurrent queue
dispatch_group_t group = dispatch_group_create(); // 创建一个group
/* 再将3个Block添加到这个queue中,并且属于同一个group */
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
dispatch_group_async(group, queue, ^{NSLog(@"blk3");});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"Done");}); // 全部结束后通知主线程,将Block追加到主线程中
dispatch_release(group); // 释放group。因为Global dispatch queue不需要我们手动释放上面的
dispatch_group_notify
函数在group中的三个block全部都执行完之后,才会将^{NSLog(@"Done");}
这一个Block追加到主线程中去。还可以使用dispatch_group_wait
函数,如下:1
2
3
4
5
6
7dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull *NSEC_PER_SEC); // 1s的时间
long result = dispatch_group_wait(group, time); // 表示group经过1s之后,是什么结果
if (result == 0) {
/* result == 0,那么group中的Block已经全部处理完 */
} else {
/* result != 0, 那么还没处理完 */
}PS:注意,执行
dispatch_gropu_wait
的线程是处于停滞状态的,只有当该函数返回后,才会执行之后的操作,所以还是推荐使用dispatch_group_notify;
,异步,无阻塞。dispatch_barrier_async
该函数和concurrent dispatch queue一起使用,看看例子
1
2
3
4
5
6
7
8dispatch_async(queue, blk_reading1); // 注意这里queue是concurrent dispatch queue
dispatch_async(queue, blk_reading2);
dispatch_async(queue, blk_reading3);
dispatch_barrier_async(queue, blk_writing);
dispatch_async(queue, blk_reading4);
dispatch_async(queue, blk_reading5);
dispatch_async(queue, blk_reading6);
dispatch_async(queue, blk_reading7);上面的代码会同时先执行blk_reading1,blk_reading2和blk_reading3这三个读取操作,然后等执行完后再单独执行blk_writing这一写入操作,最后等写入完成后再开始同时执行之后的4的读取操作。
dispatch_sync
该函数同步执行block,也就是说block不执行完,就一直停留在这个函数这里,容易导致死锁。看看例子:
1
2dispatch_queue_t queue = dispatch_get_main_queue(); // 注意,这段代码在主线程上执行。获取主线程
dispatch_sync(queue, block); // 死锁dispatch_sync
在主线程上将block追加到主线程去处理,这时候程序一直卡在dispatch_sync
这里。因为该函数一直在等待block处理结束;但是block的处理也是在主线程上,需要等当前这些操作结束后,才会去执行block。这样就卡住了,死锁,GG。dispatch_apply
该函数同样要等待block执行完,才结束。它可以指定block的执行次数。
dispatch_suspend(queue)/dispatch_resume(queue)
dispatch_suspend(queue);
:挂起queue,未执行的block暂停dispatch_resume(queue);
:恢复queue,继续执行之前没有执行的block
一些其他API
- Dispatch Semaphore:更细粒度的多线程控制,类似于信号量
dispatch_once
:整个应用程序中只执行一次,多线程安全,单例模式- Dispatch I/O:文件分块读取,提高文件读取效率