-
GCD中有2个核心概念
- 任务:执行什么操作
- 队列:用来存放任务
-
执行任务
- 同步方法: dispatch_sync
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
queue:队列 block:任务
- 异步方法: dispatch_async
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
- 同步和异步的区别
- 同步:只能在当前线程中执行任务,不具备开启新线程的能力
- 异步:可以在新的线程中执行任务,具备开启新线程的能力
- 同步方法: dispatch_sync
-
队列
- 并发队列
- 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
- 并发功能只有在异步(dispatch_async)函数下才有效
- 串行队列
- 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
- 并发队列
-
注意点
- 同步和异步主要影响:能不能开启新的线程
- 同步:只是在当前线程中执行任务,不具备开启新线程的能力
- 异步:可以在新的线程中执行任务,具备开启新线程的能力
- 并发和串行主要影响:任务的执行方式
- 并发:允许多个任务并发(同时)执行
- 串行:一个任务执行完毕后,再执行下一个任务
- 同步和异步主要影响:能不能开启新的线程
-
各种任务队列搭配
- 同步 + 串行
-
/* 同步 + 串行: 不会开启新的线程 注意点: 如果是调用 同步函数, 那么会等同步函数中的任务执行完毕, 才会执行后面的代码 */ - (void)syncSerial { // 1.创建一个串行队列 // #define DISPATCH_QUEUE_SERIAL NULL // 所以可以直接传NULL dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", NULL); // 2.将任务添加到队列中 dispatch_sync(queue, ^{ NSLog(@"任务1 == %@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"任务2 == %@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"任务3 == %@", [NSThread currentThread]); }); NSLog(@"---------"); }
- 同步 + 并发 *
/* 同步 + 并发 : 不会开启新的线程 妻管严 */ - (void)syncConCurrent { // 1.创建一个并发队列 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 2.将任务添加到队列中 dispatch_sync(queue, ^{ NSLog(@"任务1 == %@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"任务2 == %@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"任务3 == %@", [NSThread currentThread]); }); NSLog(@"---------"); }
- 异步 + 串行 *
/* 异步 + 串行:会开启新的线程 但是只会开启一个新的线程 注意: 如果调用 异步函数, 那么不用等到函数中的任务执行完毕, 就会执行后面的代码 */ - (void)asynSerial { // 1.创建串行队列 dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_SERIAL); /* 能够创建新线程的原因: 我们是使用"异步"函数调用 只创建1个子线程的原因: 我们的队列是串行队列 */ // 2.将任务添加到队列中 dispatch_async(queue, ^{ NSLog(@"任务1 == %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"任务2 == %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"任务3 == %@", [NSThread currentThread]); }); NSLog(@"--------"); }
- 异步 + 并发 *
/* 异步 + 并发 : 会开启新的线程 如果任务比较多, 那么就会开启多个线程 */ - (void)asynConcurrent { /* 执行任务 dispatch_async dispatch_sync */ /* 第一个参数: 队列的名称 第二个参数: 告诉系统需要创建一个并发队列还是串行队列 DISPATCH_QUEUE_SERIAL :串行 DISPATCH_QUEUE_CONCURRENT 并发 */ // dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_CONCURRENT); // 系统内部已经给我们提供好了一个现成的并发队列 /* 第一个参数: iOS8以前是优先级, iOS8以后是服务质量 iOS8以前 * - DISPATCH_QUEUE_PRIORITY_HIGH 高优先级 2 * - DISPATCH_QUEUE_PRIORITY_DEFAULT: 默认的优先级 0 * - DISPATCH_QUEUE_PRIORITY_LOW: 低优先级 -2 * - DISPATCH_QUEUE_PRIORITY_BACKGROUND: iOS8以后 * - QOS_CLASS_USER_INTERACTIVE 0x21 用户交互(用户迫切想执行任务) * - QOS_CLASS_USER_INITIATED 0x19 用户需要 * - QOS_CLASS_DEFAULT 0x15 默认 * - QOS_CLASS_UTILITY 0x11 工具(低优先级, 苹果推荐将耗时操作放到这种类型的队列中) * - QOS_CLASS_BACKGROUND 0x09 后台 * - QOS_CLASS_UNSPECIFIED 0x00 没有设置 第二个参数: 废物 */ dispatch_queue_t queue = dispatch_get_global_queue(0, 0); /* 第一个参数: 用于存放任务的队列 第二个参数: 任务(block) GCD从队列中取出任务, 遵循FIFO原则 , 先进先出 输出的结果和苹果所说的原则不符合的原因: CPU可能会先调度其它的线程 能够创建新线程的原因: 我们是使用"异步"函数调用 能够创建多个子线程的原因: 我们的队列是并发队列 */ dispatch_async(queue, ^{ NSLog(@"任务1 == %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"任务2 == %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"任务3 == %@", [NSThread currentThread]); }); }
- 异步 + 主队列 *
异步 + 主队列 : 不会创建新的线程, 并且任务是在主线程中执行 */ - (void)asyncMain { // 主队列: // 特点: 只要将任务添加到主队列中, 那么任务"一定"会在主线程中执行 无论你是调用同步函数还是异步函数 dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(queue, ^{ NSLog(@"%@", [NSThread currentThread]); }); }
- 同步 + 主队列 *
/* 如果是在主线程中调用同步函数 + 主队列, 那么会导致死锁 导致死锁的原因: sync函数是在主线程中执行的, 并且会等待block执行完毕. 先调用 block是添加到主队列的, 也需要在主线程中执行. 后调用 */ - (void)syncMain { NSLog(@"%@", [NSThread currentThread]); // 主队列: dispatch_queue_t queue = dispatch_get_main_queue(); // 如果是调用 同步函数, 那么会等同步函数中的任务执行完毕, 才会执行后面的代码 // 注意: 如果dispatch_sync方法是在主线程中调用的, 并且传入的队列是主队列, 那么会导致死锁 dispatch_sync(queue, ^{ NSLog(@"----------"); NSLog(@"%@", [NSThread currentThread]); }); NSLog(@"----------"); }
-
GCD线程间通信
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行耗时的异步操作...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主线程,执行UI刷新操作
});
});
- GCD其它用法
- 延时执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后执行这里的代码...
});
- 一次性代码
- 使用dispatch_once函数能保证某段代码在
程序运行过程中
只被执行1次 -
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{
// 只执行1次的代码(这里面默认是线程安全的)
});
- 使用dispatch_once函数能保证某段代码在
- 快速迭代
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){
// 执行10次代码,index顺序不确定
});
- barrier (栅栏)
- 在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
不能是全局的并发队列
所有的任务都必须在一个队列中
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
- 队列组
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程...
});