相比较NSOperation和NSThread,GCD提供了更简单的操作实现多线程,多线程下也无需创建自动释放池,而且GCD开发只有两个步骤
1 创建队列
2 提交任务到队列
队列
GCD创建的队列有两种,一种是串行队列,一种是并行队列,在串行队列中每次只执行一个任务,依次执行下去,而在并行队列中每次可以同时执行多个任务
//获取当前的队列 dispatch_queue_t queue = dispatch_get_current_queue();
可以通过以下两种方式可以获得串行队列
dispatch_queue_t serialqueue1 = dispatch_get_main_queue(); dispatch_queue_t serialqueue2 = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL); //获取队列字符串标签 char *label = dispatch_queue_get_label(serialqueue2);
第一种方式获取主线程关联的队列,第二种方式自己创建了一个串行队列
通过以下两种方式可以获得并行队列
//获取系统全局并发队列 //第一个参数接受DISPATCH_QUEUE_PRIORITY_HIGH DISPATCH_QUEUE_PRIORITY_DEFAULT DISPATCH_QUEUE_PRIORITY_LOW DISPATCH_QUEUE_PRIORITY_BACKGROUND //第二个参数为了以后扩展,只需传入0 dispatch_queue_t concurrent1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //自己创建并发队列 dispatch_queue_t concurrent2 = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT);
任务提交
任务提交有两种方式,同步提交和异步提交
可以使用代码块或者函数的方式实现任务的异步提交
当我们把任务异步提交到并行队列,主线程串行队列以及自定义串行队列中时会有以下不同的结果
异步提交代码一:(自定义串行队列)
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. //创建串行队列 dispatch_queue_t serialqueue = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL);//异步提交到串行队列 dispatch_async(serialqueue, ^{ NSLog(@"%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }); dispatch_async(serialqueue, ^{ NSLog(@"%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:3]; });
[NSThread sleepForTimeInterval:2]; NSLog(@"main:%@",[NSThread currentThread]); }
2014-10-31 15:39:06.242 IOS多线程[15238:1403] <NSThread: 0x8e39b60>{name = (null), num = 2}
2014-10-31 15:39:08.243 IOS多线程[15238:90b] main:<NSThread: 0x8b4bf00>{name = (null), num = 1}
2014-10-31 15:39:09.243 IOS多线程[15238:1403] <NSThread: 0x8e39b60>{name = (null), num = 2}
分析:上面代码一共有两条队列,我们自己创建的串行队列以及主线程队列,异步提交dispatch_async会立即返回不会阻塞主线程后面的代码,所以两条队列异步执行,又因为serialqueue是串行队列,所以我们提交的任务依次执行。
异步提交代码二:(主线程串行队列)
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. dispatch_queue_t mainqueue = dispatch_get_main_queue(); //异步提交到串行队列 dispatch_async(mainqueue, ^{ NSLog(@"task1:%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }); dispatch_async(mainqueue, ^{ NSLog(@"task2:%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }); [NSThread sleepForTimeInterval:2]; NSLog(@"main:%@",[NSThread currentThread]); }
2014-10-31 16:00:02.820 IOS多线程[15350:90b] main:<NSThread: 0x8b799c0>{name = (null), num = 1}
2014-10-31 16:00:02.835 IOS多线程[15350:90b] task1:<NSThread: 0x8b799c0>{name = (null), num = 1}
2014-10-31 16:00:05.837 IOS多线程[15350:90b] task2:<NSThread: 0x8b799c0>{name = (null), num = 1}
分析:异步添加两个任务到主线程队列(串行队列),dispatch_async会立刻返回,主线程程序执行完后会依次执行添加到主线程队列的任务
异步提交代码三(并行队列)
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. //创建并发队列 dispatch_queue_t concurrentqueue = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT); //异步提交到串行队列 dispatch_async(concurrentqueue, ^{ NSLog(@"task1:%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }); dispatch_async(concurrentqueue, ^{ NSLog(@"task2:%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }); [NSThread sleepForTimeInterval:2]; NSLog(@"main:%@",[NSThread currentThread]); }
2014-10-31 16:05:03.272 IOS多线程[15385:3607] task2:<NSThread: 0x8b76200>{name = (null), num = 3}
2014-10-31 16:05:03.272 IOS多线程[15385:1403] task1:<NSThread: 0x8e29f00>{name = (null), num = 2}
2014-10-31 16:05:05.273 IOS多线程[15385:90b] main:<NSThread: 0x8b3c2a0>{name = (null), num = 1}
分析:上面创建了两个队列,异步提交dispatch_async立即返回不会阻塞主线程后面的代码,所以两个队列异步执行,又因为我们创建的队列是并行队列,所以我们提交到队列中的两个任务也会并行执行
同步提交也有代码块和函数两种方式
我们把任务同步提交给并行队列,主线程队列以及自定义队列时会有以下不同结果
同步提交代码一:(自定义串行队列)
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. //创建串行队列 dispatch_queue_t serialqueue = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL); //同步提交到串行队列 dispatch_sync(serialqueue, ^{ NSLog(@"task1:%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }); dispatch_sync(serialqueue, ^{ NSLog(@"task2:%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }); [NSThread sleepForTimeInterval:2]; NSLog(@"main:%@",[NSThread currentThread]); }
2014-10-31 16:17:50.113 IOS多线程[15438:90b] task1:<NSThread: 0x8b7cfb0>{name = (null), num = 1}
2014-10-31 16:17:53.114 IOS多线程[15438:90b] task2:<NSThread: 0x8b7cfb0>{name = (null), num = 1}
2014-10-31 16:17:58.117 IOS多线程[15438:90b] main:<NSThread: 0x8b7cfb0>{name = (null), num = 1}
分析:因为使用了同步提交dispatch_sync,该函数只有当提交的任务执行完才会返回继续执行后面的代码
同步提交代码二:(主线程串行队列)
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. dispatch_queue_t mainqueue = dispatch_get_main_queue(); //同步提交到串行队列 dispatch_sync(mainqueue, ^{ NSLog(@"task1:%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }); dispatch_sync(mainqueue, ^{ NSLog(@"task2:%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }); [NSThread sleepForTimeInterval:2]; NSLog(@"main:%@",[NSThread currentThread]); }
分析:以上代码是错误的会产生死锁,因为通过dispatch_sync提交任务会等执行完才返回,而任务又被提交到了主线程串行队列,所以会先等待主线程代码执行完,主线程又在等待dispatch_sync返回,两者相互等待对方执行,所以产生了死锁。
同步提交代码三:(并行队列)
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. //创建并发队列 dispatch_queue_t concurrentqueue = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT); //同步提交到串行队列 dispatch_sync(concurrentqueue, ^{ NSLog(@"task1:%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }); dispatch_sync(concurrentqueue, ^{ NSLog(@"task2:%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }); [NSThread sleepForTimeInterval:2]; NSLog(@"main:%@",[NSThread currentThread]); }
2014-10-31 16:21:01.445 IOS多线程[15453:90b] task1:<NSThread: 0x8b769a0>{name = (null), num = 1}
2014-10-31 16:21:04.447 IOS多线程[15453:90b] task2:<NSThread: 0x8b769a0>{name = (null), num = 1}
2014-10-31 16:21:09.449 IOS多线程[15453:90b] main:<NSThread: 0x8b769a0>{name = (null), num = 1}
分析:因为使用了同步提交dispatch_sync,该函数只有当提交的任务执行完才会返回继续执行后面的代码,所以当使用同步提交时,并行队列和自定义的串行队列作用是一样的
GCD其他常用方法
执行多个操作:dispatch_apply
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. //串行队列 dispatch_queue_t serialqueue = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL); //主线程队列 dispatch_queue_t mainqueue = dispatch_get_main_queue(); //并发队列 dispatch_queue_t concurrentqueue = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT); dispatch_apply(5, concurrentqueue, ^(size_t time) { NSLog(@"第%zu此执行,%@",time,[NSThread currentThread]); }); NSLog(@"main"); }
输出
2014-10-31 19:44:26.816 IOS多线程[15709:3a03] 第3此执行,<NSThread: 0x9b68ce0>{name = (null), num = 4}
2014-10-31 19:44:26.816 IOS多线程[15709:1403] 第0此执行,<NSThread: 0x8b7a410>{name = (null), num = 2}
2014-10-31 19:44:26.816 IOS多线程[15709:90b] 第1此执行,<NSThread: 0x8b7a2d0>{name = (null), num = 1}
2014-10-31 19:44:26.816 IOS多线程[15709:3903] 第2此执行,<NSThread: 0x996a630>{name = (null), num = 3}
2014-10-31 19:44:26.817 IOS多线程[15709:3a03] 第4此执行,<NSThread: 0x9b68ce0>{name = (null), num = 4}
2014-10-31 19:44:26.818 IOS多线程[15709:90b] main
如果把dispatch_apply队列换成串行队列输出如下
2014-10-31 19:52:07.125 IOS多线程[15724:90b] 第0此执行,<NSThread: 0x8b28ad0>{name = (null), num = 1}
2014-10-31 19:52:07.126 IOS多线程[15724:90b] 第1此执行,<NSThread: 0x8b28ad0>{name = (null), num = 1}
2014-10-31 19:52:07.126 IOS多线程[15724:90b] 第2此执行,<NSThread: 0x8b28ad0>{name = (null), num = 1}
2014-10-31 19:52:07.126 IOS多线程[15724:90b] 第3此执行,<NSThread: 0x8b28ad0>{name = (null), num = 1}
2014-10-31 19:52:07.127 IOS多线程[15724:90b] 第4此执行,<NSThread: 0x8b28ad0>{name = (null), num = 1}
2014-10-31 19:52:07.127 IOS多线程[15724:90b] main
如果换成主线程队列则会产生死锁
只执行一次任务(可以用来实现单例模式):
static dispatch_once_t onceToken; dispatch_once(&onceToken,^{ //执行代码块 });
onceToken用来判断代码块是否已经执行过,它的本质是一个long型整数
指定时间点执行任务:
dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);
分组:
如果希望队列中所有任务结束后执行某个操作(比如执行3个下载任务,全部完成后通知界面更新),那么可以使用分组
//主线程队列 dispatch_queue_t mainqueue = dispatch_get_main_queue(); //并发队列 dispatch_queue_t concurrentqueue = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, concurrentqueue, ^{ NSLog(@"task1"); }); dispatch_group_async(group, concurrentqueue, ^{ NSLog(@"task2"); }); dispatch_group_notify(group, mainqueue, ^{ NSLog(@"updateui"); });
上面代码当task1和task2都执行完后才会执行notify中的任务
分界:
分界是指同一队列中当前面添加的任务执行完才会执行分界任务,而且只有当分界任务执行完才会执行后面添加的任务
dispatch_queue_t concurrentqueue = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(concurrentqueue, ^{ NSLog(@"task1"); }); dispatch_async(concurrentqueue, ^{ NSLog(@"task2"); }); dispatch_barrier_async(concurrentqueue, ^{ NSLog(@"barriertasck"); }); dispatch_async(concurrentqueue, ^{ NSLog(@"lasttask"); });
上面代码只有当task1和task2执行完才会执行分解任务barriertask,当分解任务执行完才会执行lasttask