GCD:Grand Central Dispatch(GCD) 是异步执行任务的技术之一。一般将应用程序中记述的线程管理用的代码在系统级中实现。开发者只需要定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。由于线程管理是作为系统的一部分来实现的,因此可以统一管理,也可执行任务,这样就比以前的线程更有效率。
1 Dispatch Queue
“Dispatch Queue”是什么,如其名称所示,是执行处理的等待队列。Dispatch Queue 按照追加的顺序执行处理。先进先出FIFO,first-in-first-out。
队列类型:
1 Serial Dispatch Queue 等待现在执行处理结束
2 Concurrent Dispatch Queue 不等待现在执行处理结束
比较两种Dispatch Queue。
dispatch_sync(queue,block1); dispatch_sync(queue,block2); dispatch_sync(queue,block3); dispatch_sync(queue,block4); dispatch_sync(queue,block5); dispatch_sync(queue,block6);当queue为serial Dispatch Queue,会先执行block1,当block1执行完毕后,接着执行block2,当block2执行完毕后,接着执行block3,如此重复。同时执行的处理数只能有一个。
block1 -> block2-> block3-> block4-> block5-> block6
当queue为Concurrent Dispatch Queue时,首先执行block1,不管block1是否结束,都开始执行后面的block2,不管block2是否结束,都开始执行block3,如此重复循环。(同时执行的处理数量取决于当前系统的状态。)
线程1 | 线程2 | 线程3 |
block1 | block2 | block3 |
block4 | block6 | block5 |
创建方式
第一种方式:dispatch_queue_create
// 创建串行队列 <span style="font-family: Arial, Helvetica, sans-serif;">DISPATCH_QUEUE_SERIAL = NULL</span> dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_SERIAL);
// 1.创建一个并发队列 dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_CONCURRENT);
虽然Serial Dispatch Queue 和Current Dispatch Queue将受到限制,但是使用dispatch_queue_create函数可生成人意多个Dispatch Queue。
当生成多个Serial Dispatch Queue时,虽然在一个Serial Dispatch Queue中同时只能执行一个追加处理,但多个Serial Dispatch Queue之间可以并行执行。
通过dispatch_queue_create 函数生成的Dispatch Queue,在使用结束后需要释放。
//释放 dispatch_release(queue);
第二种方式:获取系统标准提供的Dispatch Queue。
Main Dispatch Queue,是在主线程中执行的Dispatch Queue,属于Serial Dispatch Queue。
// 1.获得主队列 dispatch_queue_t mainQueue = dispatch_get_main_queue();追加到Main Dispatch Queue的处理在主线程的RunLoop中执行。因此要将用户界面的更新等一些必须在主线程中执行的处理追加到Main Dispatch Queue使用。与NSObject类的persormSelectorOnMainThread实例方法相同。
Global Dispatch Queue是ConCurrent Dispatch Queue。所有应用程序都可以使用,没有必要通过dispatch_queue_create函数逐个生成Concurrent Dispatch Queue。
// 1.获得全局的并发队列 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);第一个参数表示优先级。Global Dispatch Queue有4个执行优先级,分别是:
#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
2 执行任务方式
GCD有两种方法执行任务。
1 同步的方式执行任务
dispatch_sync(dispatch_queue_t queue,dispatch_block_t
block);
2 异步的方式执行任务
dispatch_async(dispatch_queue_t queue,dispatch_block_t block);
同步和异步的区别
同步:只能在当前线程中执行任务,不具备开启新线程的能力
异步:可以在新的线程中执行任务,具备开启新线程的能力
串行队列 同步函数
/** * 串行队列 + 同步函数:不会开启新的线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务 */ -(void)syncSerial{ NSLog(@"syncSerial ----- begin"); //创建串行队列 serial dispatch_queue_t serialQueue = dispatch_queue_create("com.vn.serial", NULL); dispatch_sync(serialQueue, ^{ NSLog(@"1...sync....%@",[NSThread currentThread]); }); dispatch_sync(serialQueue, ^{ NSLog(@"2...sync....%@",[NSThread currentThread]); }); dispatch_sync(serialQueue, ^{ NSLog(@"3...sync....%@",[NSThread currentThread]); }); NSLog(@"syncSerial ----- end"); }打印:
2016-10-19 15:36:43.810 GCD基本使用[9712:1396171] syncSerial ----- begin 2016-10-19 15:36:43.810 GCD基本使用[9712:1396171] 1...sync....<NSThread: 0x60000006bd40>{number = 1, name = main} 2016-10-19 15:36:43.811 GCD基本使用[9712:1396171] 2...sync....<NSThread: 0x60000006bd40>{number = 1, name = main} 2016-10-19 15:36:43.811 GCD基本使用[9712:1396171] 3...sync....<NSThread: 0x60000006bd40>{number = 1, name = main} 2016-10-19 15:36:43.811 GCD基本使用[9712:1396171] syncSerial ----- end
串行队列 异步函数
/** * 串行队列 + 异步函数 :会开启新的线程,但是任务是串行的,执行完一个任务,再执行下一个任务 */ -(void)asyncSerial{ NSLog(@"asyncSerial ----- begin"); //创建串行队列 serial dispatch_queue_t serialQueue = dispatch_queue_create("com.vn.serial", NULL); dispatch_async(serialQueue, ^{ NSLog(@"1...async....%@",[NSThread currentThread]); }); dispatch_async(serialQueue, ^{ NSLog(@"2...async....%@",[NSThread currentThread]); }); dispatch_async(serialQueue, ^{ NSLog(@"3...async....%@",[NSThread currentThread]); }); NSLog(@"asyncSerial ----- end"); }
打印:
2016-10-19 15:38:55.354 GCD基本使用[9730:1397289] asyncSerial ----- begin 2016-10-19 15:38:55.354 GCD基本使用[9730:1397289] asyncSerial ----- end 2016-10-19 15:38:55.354 GCD基本使用[9730:1397758] 1...async....<NSThread: 0x608000070d40>{number = 3, name = (null)} 2016-10-19 15:38:55.355 GCD基本使用[9730:1397758] 2...async....<NSThread: 0x608000070d40>{number = 3, name = (null)} 2016-10-19 15:38:55.355 GCD基本使用[9730:1397758] 3...async....<NSThread: 0x608000070d40>{number = 3, name = (null)}
主队列 同步函数
/** * 主队列 + 同步函数: 死锁,不应该这样使用 */ - (void)syncMain{ NSLog(@"syncMain ----- begin"); // 1.获得主队列 dispatch_queue_t mainQueue = dispatch_get_main_queue(); dispatch_sync(mainQueue, ^{ NSLog(@"1--syncMain---%@", [NSThread currentThread]); }); NSLog(@"syncMain ----- end"); }只输出下面数据后就会卡死。
syncMain ----- begin
主队列 异步函数
/** * 主队列 + 异步函数: 在主线程中执行任务,不会阻塞主线程。任务是串行的,执行完一个任务,再执行下一个任务 */ - (void)asyncMain{ NSLog(@"asyncMain ----- begin"); // 1.获得主队列 dispatch_queue_t mainQueue = dispatch_get_main_queue(); dispatch_async(mainQueue, ^{ NSLog(@"1--asyncMain---%@", [NSThread currentThread]); }); dispatch_async(mainQueue, ^{ NSLog(@"2--asyncMain---%@", [NSThread currentThread]); }); dispatch_async(mainQueue, ^{ NSLog(@"3--asyncMain---%@", [NSThread currentThread]); }); NSLog(@"asyncMain ----- end"); }打印:
2016-10-19 15:39:56.747 GCD基本使用[9750:1398368] asyncMain ----- begin 2016-10-19 15:39:56.748 GCD基本使用[9750:1398368] asyncMain ----- end 2016-10-19 15:39:56.748 GCD基本使用[9750:1398368] 1--asyncMain---<NSThread: 0x60000006ab80>{number = 1, name = main} 2016-10-19 15:39:56.753 GCD基本使用[9750:1398368] 2--asyncMain---<NSThread: 0x60000006ab80>{number = 1, name = main} 2016-10-19 15:39:56.753 GCD基本使用[9750:1398368] 3--asyncMain---<NSThread: 0x60000006ab80>{number = 1, name = main}
并行队列 同步函数
/** * 并发队列 + <span style="font-family: Arial, Helvetica, sans-serif;">同步函数</span><span style="font-family: Arial, Helvetica, sans-serif;">:不会开启新的线程,由于不开启新线程,需要等待执行完,所以任务执行完一个,再执行下一个。</span> */ - (void)syncConcurrent{ // 1.获得全局的并发队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 2.将任务加入队列 dispatch_sync(queue, ^{ NSLog(@"1--syncConcurrent---%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"2--syncConcurrent---%@", [NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"3--syncConcurrent---%@", [NSThread currentThread]); }); NSLog(@"syncConcurrent--------end"); }打印:
2016-10-19 15:52:30.536 GCD基本使用[9790:1404534] syncConcurrent--------begin 2016-10-19 15:52:30.536 GCD基本使用[9790:1404534] 1--syncConcurrent---<NSThread: 0x60000006b140>{number = 1, name = main} 2016-10-19 15:52:30.536 GCD基本使用[9790:1404534] 2--syncConcurrent---<NSThread: 0x60000006b140>{number = 1, name = main} 2016-10-19 15:52:30.537 GCD基本使用[9790:1404534] 3--syncConcurrent---<NSThread: 0x60000006b140>{number = 1, name = main} 2016-10-19 15:52:30.537 GCD基本使用[9790:1404534] syncConcurrent--------end
并行队列 异步函数
/** * 并发队列 + 异步函数:可以同时开启多条线程 */ - (void)asyncConcurrent{ NSLog(@"asyncConcurrent--------begin"); // 1.获得全局的并发队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 2.将任务加入队列 dispatch_async(queue, ^{ NSLog(@"1--asyncConcurrent---%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"2--asyncConcurrent---%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"3--asyncConcurrent---%@", [NSThread currentThread]); }); NSLog(@"asyncConcurrent--------end"); }打印:
2016-10-19 15:55:34.637 GCD基本使用[9820:1406397] asyncConcurrent--------begin 2016-10-19 15:55:34.637 GCD基本使用[9820:1406397] asyncConcurrent--------end 2016-10-19 15:55:34.637 GCD基本使用[9820:1406608] 1--asyncConcurrent---<NSThread: 0x600000069e00>{number = 3, name = (null)} 2016-10-19 15:55:34.637 GCD基本使用[9820:1406613] 2--asyncConcurrent---<NSThread: 0x608000077a00>{number = 4, name = (null)} 2016-10-19 15:55:34.637 GCD基本使用[9820:1406614] 3--asyncConcurrent---<NSThread: 0x600000072680>{number = 5, name = (null)}
3 线程间通信
dispatch_async(dispatch_get_main_queue(), ^{//});
- (void)threadCommunication{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 图片的网络路径 NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"]; // 加载图片 NSData *data = [NSData dataWithContentsOfURL:url]; // 生成图片 UIImage *image = [UIImage imageWithData:data]; // 回到主线程 dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = image; }); }); }
4 延时
// 延时追加 - (void)delay{ NSLog(@"delay-----begin"); //延时1s dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC); dispatch_after(time, dispatch_get_main_queue(), ^{ NSLog(@"after------%@",[NSThread currentThread]); }); NSLog(@"delay-----end"); }打印:
2016-10-19 16:35:45.680 GCD基本使用[9932:1427078] delay-----begin 2016-10-19 16:35:45.680 GCD基本使用[9932:1427078] delay-----end 2016-10-19 16:35:46.752 GCD基本使用[9932:1427078] after------<NSThread: 0x6000000635c0>{number = 1, name = main}dispatch_after 并不是延时指定时间后执行处理,而是延时指定时间后追加任务到Dispatch Queue。
其中dispatch_time(DISPATCH_TIME_NOW,1ull*NSEC_PER_SEC);表示从现在开始1s后时间的值。
DISPATCH_TIME_NOW 表示当前的时间。
第二个参数单位为纳秒。
#define NSEC_PER_SEC 1000000000ull //每秒有多少纳秒 #define NSEC_PER_MSEC 1000000ull //每毫秒有多少纳秒 #define USEC_PER_SEC 1000000ull //每秒有多少微秒 #define NSEC_PER_USEC 1000ull //每微秒有多少纳秒延时1s写法:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC); dispatch_time_t time1 = dispatch_time(DISPATCH_TIME_NOW, 1000ull*NSEC_PER_MSEC); dispatch_time_t time2 = dispatch_time(DISPATCH_TIME_NOW, 1000ull*USEC_PER_SEC);
5 Dispatch Group
开发中会有这种需求,执行多个任务,只有这几个任务都执行完成后才执行最终任务。这种情况可以使用Dispatch Queue。
- (void)group{ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 创建一个队列组 dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ for (int i=0; i<5; i++) { NSLog(@"任务1-------%d",i); } }); dispatch_group_async(group, queue, ^{ for (int i=0; i<5; i++) { NSLog(@"任务2-------%d",i); } }); dispatch_group_notify(group, queue, ^{ NSLog(@"end....."); }); }打印:
2016-10-19 18:05:56.138 GCD基本使用[10052:1471672] 任务1-------0 2016-10-19 18:05:56.138 GCD基本使用[10052:1471674] 任务2-------0 2016-10-19 18:05:56.139 GCD基本使用[10052:1471674] 任务2-------1 2016-10-19 18:05:56.139 GCD基本使用[10052:1471672] 任务1-------1 2016-10-19 18:05:56.139 GCD基本使用[10052:1471674] 任务2-------2 2016-10-19 18:05:56.139 GCD基本使用[10052:1471672] 任务1-------2 2016-10-19 18:05:56.139 GCD基本使用[10052:1471674] 任务2-------3 2016-10-19 18:05:56.139 GCD基本使用[10052:1471672] 任务1-------3 2016-10-19 18:05:56.139 GCD基本使用[10052:1471674] 任务2-------4 2016-10-19 18:05:56.139 GCD基本使用[10052:1471672] 任务1-------4 2016-10-19 18:05:56.139 GCD基本使用[10052:1471672] end....
6 dispatch_barrier_async
在访问数据库或文件时,写入处理确实不可与其他的写入处理以及其他包含读写的处理并行执行。但是读取处理只是和读取处理并行执行就不会发生问题。
为了高效的进行访问,读取处理追加到Concurrent Dispatch Queue中,写入处理在一个读取都没有的情况下,追加到Serial Dispatch Queue中可。利用Dispatch Group和dispatch_set_target_queue也可以实现,但是会很复杂。
GCD提供了简便方法——dispatch_barrier_async函数。
- (void)barrier{ dispatch_queue_t queue = dispatch_queue_create("abcd", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"----1-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"----2-----%@", [NSThread currentThread]); }); dispatch_barrier_async(queue, ^{ NSLog(@"----barrier-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"----3-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"----4-----%@", [NSThread currentThread]); }); }打印:
2016-10-19 19:18:30.739 GCD基本使用[10096:1490892] ----2-----<NSThread: 0x600000073d40>{number = 4, name = (null)} 2016-10-19 19:18:30.739 GCD基本使用[10096:1490890] ----1-----<NSThread: 0x608000075300>{number = 3, name = (null)} 2016-10-19 19:18:30.740 GCD基本使用[10096:1490890] ----barrier-----<NSThread: 0x608000075300>{number = 3, name = (null)} 2016-10-19 19:18:30.740 GCD基本使用[10096:1490890] ----3-----<NSThread: 0x608000075300>{number = 3, name = (null)} 2016-10-19 19:18:30.740 GCD基本使用[10096:1490892] ----4-----<NSThread: 0x600000073d40>{number = 4, name = (null)}在前两个任务执行完后,才会追加处理到该queue中。然后当该处理执行完毕后,开始追加其他处理。
7 dispatch_apply
- (void)apply{ NSLog(@"apply------begin"); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //该函数按指定的次数 将block追加到Dispatch Queue中。并等待全部处理执行结束。 dispatch_apply(10, queue, ^(size_t index) { NSLog(@"apply------%zu",index); }); NSLog(@"apply------end"); }打印:
2016-10-19 19:45:19.186 GCD基本使用[10114:1500525] apply------begin 2016-10-19 19:45:19.187 GCD基本使用[10114:1500525] apply------1 2016-10-19 19:45:19.187 GCD基本使用[10114:1500833] apply------0 2016-10-19 19:45:19.187 GCD基本使用[10114:1500837] apply------2 2016-10-19 19:45:19.187 GCD基本使用[10114:1500838] apply------3 2016-10-19 19:45:19.187 GCD基本使用[10114:1500525] apply------4 2016-10-19 19:45:19.187 GCD基本使用[10114:1500833] apply------5 2016-10-19 19:45:19.187 GCD基本使用[10114:1500837] apply------6 2016-10-19 19:45:19.187 GCD基本使用[10114:1500838] apply------7 2016-10-19 19:45:19.188 GCD基本使用[10114:1500525] apply------8 2016-10-19 19:45:19.188 GCD基本使用[10114:1500833] apply------9 2016-10-19 19:45:19.188 GCD基本使用[10114:1500525] apply------end可以使用该函数遍历NSArray对象,
dispatch_apply([array count],queue,^(size_t index){});
由于dispatch_apply函数与dispatch_sync相同,会等待处理执行结束,因此推荐在dispatch_async函数中异步执行dispatch_apply函数。
- (void)apply1{ NSLog(@"apply1------begin"); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ dispatch_apply(5, queue, ^(size_t index) { NSLog(@"apply------%zu",index); }); dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"done"); }); }); NSLog(@"apply1------end"); }打印:
2016-10-19 20:26:16.245 GCD基本使用[10143:1518627] apply1------begin 2016-10-19 20:26:16.246 GCD基本使用[10143:1518627] apply1------end 2016-10-19 20:26:16.246 GCD基本使用[10143:1518924] apply------0 2016-10-19 20:26:16.246 GCD基本使用[10143:1518928] apply------1 2016-10-19 20:26:16.246 GCD基本使用[10143:1518929] apply------2 2016-10-19 20:26:16.246 GCD基本使用[10143:1518930] apply------3 2016-10-19 20:26:16.246 GCD基本使用[10143:1518924] apply------4 2016-10-19 20:26:16.251 GCD基本使用[10143:1518627] done
8 dispatch_once
dispatch_once 函数保证应用程序中只执行一次。
- (void)once{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"------run"); }); }