GCD全称 Grand Central Dispatch
- 是纯C语言,是一套底层API,提供了非常多强大的函数,来进行系统线程的管理,它提供了一种新的方法来进行并发程序编写
- 从基本功能上讲,GCD有点像NSOperationQueue,他们都允许程序将任务切分为多个单一任务然后提交至工作队列来并发地或者串行地执行
- GCD比之NSOpertionQueue更底层更高效,并且它不是Cocoa框架的一部分
优势:GCD是苹果公司为多核的并行运算提出的解决方案
- 易用: GCD比之thread跟简单易用。由于GCD基于work unit而非像thread那样基于运算,所以GCD可以控制诸如等待任务结束、监视文件描述符、周期执行代码以及工作挂起等任务。基于block的血统导致它能极为简单得在不同代码作用域之间传递上下文
- 性能: GCD会自动利用更多的CPU内核,会自动管理线程的生命周期(创建线程、调度任务、销毁线程),程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
GCD的两个核心:
- 队列:GCD会从队列中取出任务,按照不同情况,放到对应的线程中执行,遵循FIFO原则
GCD的四种队列:
- 主队列(是串行队列)
// 在主线程中顺序执行,有死锁现象(主队列同步执行的情况)
dispatch_queue_t mainQueue = dispatch_get_main_queue();
- 全局并行队列 (本质就是并发队列,没有名称,不可跟踪)
// 通过函数 dispatch_get_global_queue(<#long identifier#>, <#unsigned long flags#>)获得,第一个参数是优先级,第二个是预留(无用),一般二者都设为0
// 获得程序进程缺省产生的并发队列,可设定优先级来选择高、中、低三个优先级队列。由于是系统默认生成的,所以无法调用dispatch_resume()和dispatch_suspend()来控制执行继续或中断。需要注意的是,三个队列不代表三个线程,可能会有更多的线程。并发队列可以根据实际情况来自动产生合理的线程数,也可理解为dispatch队列实现了一个线程池的管理,对于程序逻辑是透明的
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- 创建串行队列 (DISPATCH_QUEUE_SERIAL)
// 生成一个串行队列,队列中的block按照先进先出(FIFO)的顺序去执行,实际上为单线程执行。第一个参数是队列的名称(字符串标识符"cn.serial.com"),在调试程序时会非常有用,所有尽量不要重名了,后者是队列类型
dispatch_queue_t queueSerial = dispatch_queue_create("cn.serial.com", DISPATCH_QUEUE_SERIAL);
- 创建并行队列 (DISPATCH_QUEUE_CONCURRENT),一般使用系统带的全局并行队列即可
// 生成一个并发执行队列,block被分发到多个线程去执行
dispatch_queue_t queueConcurrent = dispatch_queue_create("cn.concurrent.com", DISPATCH_QUEUE_CONCURRENT);
- 任务:就是要处理的事情
任务处理(操作)方式有两种:
- 同步 :在当前线程执行,不开辟新的线程
// 同步执行block,函数不返回,一直等到block执行完毕。编译器会根据实际情况优化代码,所以有时候你会发现block其实还在当前线程上执行,并没用产生新线程
dispatch_sync(queue, ^{
//block具体代码
});
- 异步 :在新的线程中执行任务,可以开启新的一条或多条线程
// 异步执行block,函数立即返回
dispatch_async(queue, ^{
//block具体代码
});
注意:
1. 尽可能避免使用dispatch_sync,嵌套使用时还容易引起程序死锁
如果queue1是一个串行队列的话,这段代码立即产生死锁:
dispatch_sync(queue1, ^{
dispatch_sync(queue1, ^{
......
});
......
});
那实际运用中,一般可以用dispatch这样来写,常见的网络请求数据多线程执行模型:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//子线程中开始网络请求数据
//更新数据模型
dispatch_sync(dispatch_get_main_queue(), ^{
//在主线程中更新UI代码
});
});
2. dispatch队列是线程安全的,可以利用串行队列实现锁的功能。比如多线程写同一数据库,需要保持写入的顺序和每次写入的完整性,简单地利用串行队列即可实现:
dispatch_queue_t queue1 = dispatch_queue_create("com.dispatch.writedb", DISPATCH_QUEUE_SERIAL);
- (void)writeDate:(NSData *)data {
dispatch_async(queue1, ^{
//write database
});
}
下一次调用writeDate:必须等到上次调用完成后才能进行,保证writeDate:方法是线程安全的
dispatch队列还实现其它一些常用函数
// 包括:
1. 快速迭代(开多个线程并发完成迭代操作)
void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t)); //重复执行block,需要注意的是这个方法是同步返回,也就是说等到所有block执行完毕才返回,如需异步返回则嵌套在dispatch_async中来使用。多个block的运行是否并发或串行执行也依赖queue的是否并发或串行。
2. 栅栏函数(控制任务的执行顺序)
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block); //这个函数可以设置同步执行的block,它会等到在它加入队列之前的block执行完毕后,才开始执行。在它之后加入队列的block,则等到这个block执行完毕后才开始执行。
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block); //同上,除了它是同步返回函数
3. 延迟执行(延迟·控制在哪个线程执行)
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block); //延迟执行block
最后再来看看dispatch队列的一个很有特色的函数:
4. 变更执行优先级
void dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);
它会把需要执行的任务对象指定到不同的队列中去处理,这个任务对象可以是dispatch队列,也可以是dispatch源。而且这个过程可以是动态的,可以实现队列的动态调度管理等等。比如说有两个队列dispatchA和dispatchB,这时把dispatchA指派到dispatchB:
dispatch_set_target_queue(dispatchA, dispatchB);
那么dispatchA上还未运行的block会在dispatchB上运行。这时如果暂停dispatchA运行:
dispatch_suspend(dispatchA);
则只会暂停dispatchA上原来的block的执行,dispatchB的block则不受影响。而如果暂停dispatchB的运行,则会暂停dispatchA的运行。
5. 一次性代码(注意不能放到懒加载)
- (void)once {
//整个程序运行过程中只会执行一次
//onceToken用来记录该部分的代码是否被执行过
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"-----");
});
}
6. 进入群组和离开群组
dispatch_group_enter(group);//执行该函数后,后面异步执行的block会被gruop监听
dispatch_group_leave(group);//异步block中,所有的任务都执行完毕,最后离开群组
//注意:dispatch_group_enter | dispatch_group_leave必须成对使
队列和任务执行方式的组合
![](https://img.haomeiwen.com/i2695489/1ddaec552e78626e.png)
六种组合:
- 同步执行+串行队(不会开辟子线程)
dispatch_sync(queueSerial, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"1--->%@", [NSThread currentThread]);
});
dispatch_sync(queueSerial, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"2--->%@", [NSThread currentThread]);
});
- 同步执行+并行队列(不会开辟子线程)
dispatch_sync(queueConcu, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"3--->%@", [NSThread currentThread]);
});
dispatch_sync(queueConcu, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"4--->%@", [NSThread currentThread]);
});
- 异步执行+串行队列(开启一条子线程,且顺序执行)
dispatch_async(queueSerial, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"5--->%@", [NSThread currentThread]);
});
dispatch_async(queueSerial, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"6--->%@", [NSThread currentThread]);
});
- 异步执行+并行队列(开启多条子线程,且并发执行)
dispatch_async(queueConcu, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"7--->%@", [NSThread currentThread]);
});
dispatch_async(queueConcu, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"8--->%@", [NSThread currentThread]);
});
- 异步执行+全局并行队列(开启多条子线程,且并发执行)
dispatch_async(globConcu, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"9--->%@", [NSThread currentThread]);
});
dispatch_async(globConcu, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"10--->%@", [NSThread currentThread]);
});
- (在主线程中)同步执行+main队列(死锁):NSLog添加到了主队列的最后,NSLog的执行需要等待主队列执行完之后执行,而主队列又在等NSLog执行完(注意与情况3的比较)
dispatch_sync(mainQueue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"11--->%@", [NSThread currentThread]);
});
dispatch_sync(mainQueue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"12--->%@", [NSThread currentThread]);
});
GCD管理线程,功能强大:
有没有新线程看是否同步——异步
有没有顺序执行看是否串行——并行
网友评论