一、GCD的使用:
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
async表明异步运行,block代表的是你要做的事情,queue则是你把任务交给谁来处理了.当程序异步执行时,如果是并发队列执行那么到底开辟多少线程有系统决定,如果是在同步队列执行的话只会开启一条子线程。
之所以程序中会用到多线程是因为程序往往会需要下载数据,然后更新UI.为了良好的用户体验,读取数据的操作会倾向于在后台运行,这样以避免阻塞主线程.
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
sync表明同步运行,block代表的是你要做的事情,queue则是你把任务交给谁来处理了.需要注意的是,同步执行时无论是在dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);(全局并发队列还是自己创建的并发队列)系统都不会帮我们开启子线程,所有操作都是在主队列执行。但切记不能在dispatch_get_main_queue()队列执行,否则会造成死锁,这时候系统不知道应该先执行上面的操作,还是先执行下面的操作。
(1)首先给大家介绍下dispatch_queue
系统默认就有一个串行队列main_queue和并行队列global_queue:
dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t mainQ = dispatch_get_main_queue();
当然我们也可以手动创建dispatch_queue:
Serial Dispatch Queue -- 线程池只提供一个子线程用来执行任务,所以后一个任务必须等到前一个任务执行结束才能开始。
验证:
dispatch_queue_t serial = dispatch_queue_create("demo", DISPATCH_QUEUE_SERIAL);
dispatch_async(serial, ^{
sleep(5);
NSLog(@"1 queue=%@",[NSThread currentThread]);
});
dispatch_async(serial, ^{
sleep(3);
NSLog(@"2 queue=%@",[NSThread currentThread]);
});
dispatch_async(serial, ^{
sleep(1);
NSLog(@"3 queue=%@",[NSThread currentThread]);
});
打印结果:
15:51:04.004 TestReplaykit[8802:223039] 1 queue={number = 2, name = (null)}
15:51:07.010 TestReplaykit[8802:223039] 2 queue={number = 2, name = (null)}
15:51:08.015 TestReplaykit[8802:223039] 3 queue={number = 2, name = (null)}
Concurrent Dispatch Queue -- 线程池提供多个线程来执行任务,所以可以按序启动多个任务并发执行。
验证:
dispatch_queue_t serial = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(serial, ^{
sleep(5);
NSLog(@"1 queue=%@",[NSThread currentThread]);
});
dispatch_async(serial, ^{
sleep(3);
NSLog(@"2 queue=%@",[NSThread currentThread]);
});
dispatch_async(serial, ^{
sleep(1);
NSLog(@"3 queue=%@",[NSThread currentThread]);
});
15:48:14.605 TestReplaykit[8746:221997] 3 queue={number = 2, name = (null)}
15:48:16.608 TestReplaykit[8746:221999] 2 queue={number = 3, name = (null)}
15:48:18.607 TestReplaykit[8746:221995] 1 queue={number = 4, name = (null)}
(2)global_queue和Main queue的简单使用:
在开发中我们之所以用到多线程,是因为很多耗时操作会阻塞主线程,造成页面假死,为了更好的用户体验我们会把这些耗时操作放到global_queue中来完成,完成后我们必须回到主线程中来刷新UI,所以在开发中凡是涉及到UI的逻辑我们都要把代码放到main_queue中来处理
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"];
NSData * data = [[NSData alloc]initWithContentsOfURL:url];
UIImage *image = [[UIImage alloc]initWithData:data];
if (data != nil) {
dispatch_async(dispatch_get_main_queue(), ^{
//回到主线程刷新UI
self.imageView.image = image;
});
}
});
(3)dispatch_group_async的使用
dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很常用,比如你执行多个下载任务,当任务都下载完成后你才通知界面说完成的了。下面是一段例子代码:
dispatch_queue_t serial = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group,serial, ^{
sleep(5);
NSLog(@"1 queue=%@",[NSThread currentThread]);
});
dispatch_group_async(group,serial, ^{
sleep(3);
NSLog(@"2 queue=%@",[NSThread currentThread]);
});
dispatch_group_async(group,serial, ^{
sleep(1);
NSLog(@"3 queue=%@",[NSThread currentThread]);
});
dispatch_group_notify(group, serial, ^{
NSLog(@"4 queue=%@",[NSThread currentThread]);
});
15:59:51.063 TestReplaykit[8978:225207] 3 queue={number = 2, name = (null)}
15:59:53.062 TestReplaykit[8978:225208] 2 queue={number = 3, name = (null)
15:59:55.062 TestReplaykit[8978:225206] 1 queue={number = 4, name = (null)}
15:59:55.063 TestReplaykit[8978:225206] 4 queue={number = 4, name = (null)}
(4)栈栏函数dispatch_barrier_async
dispatch_queue_t serial = dispatch_get_global_queue(0, 0);
dispatch_async(serial, ^{
sleep(5);
NSLog(@"1 queue=%@",[NSThread currentThread]);
});
dispatch_async(serial, ^{
sleep(3);
NSLog(@"2 queue=%@",[NSThread currentThread]);
});
dispatch_barrier_async(serial, ^{
sleep(1);
NSLog(@"4 queue=%@",[NSThread currentThread]);
});
dispatch_async(serial, ^{
sleep(1);
NSLog(@"3 queue=%@",[NSThread currentThread]);
});
运行结果:
16:40:24.026 TestReplaykit[9775:234799] 4 queue={number = 2, name = (null)}
16:40:24.026 TestReplaykit[9775:234802] 3 queue={number = 3, name = (null)}
16:40:26.025 TestReplaykit[9775:234798] 2 queue={number = 4, name = (null)}
16:40:28.025 TestReplaykit[9775:234800] 1 queue={number = 5, name = (null)}
如果使用dispatch_queue_t serial = dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL);
根据FIFO原则肯定为顺序执行,感兴趣的同学可以自己验证下。
如果使用dispatch_queue_t serial = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t serial = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(serial, ^{
sleep(5);
NSLog(@"1 queue=%@",[NSThread currentThread]);
});
dispatch_async(serial, ^{
sleep(3);
NSLog(@"2 queue=%@",[NSThread currentThread]);
});
dispatch_barrier_async(serial, ^{
sleep(1);
NSLog(@"4 queue=%@",[NSThread currentThread]);
});
dispatch_async(serial, ^{
sleep(1);
NSLog(@"3 queue=%@",[NSThread currentThread]);
});
16:45:07.788 TestReplaykit[9878:236344] 2 queue={number = 2, name = (null)}
16:45:09.788 TestReplaykit[9878:236347] 1 queue={number = 3, name = (null)}
16:45:10.792 TestReplaykit[9878:236344] 4 queue={number = 2, name = (null)}
16:45:11.795 TestReplaykit[9878:236347] 3 queue={number = 3, name = (null)}
由上我们可以得出一个结论,我们可以通过栈栏函数来控制子线程执行顺序,但是queue不能是系统的global_queue,只能是自己创建的queue_create类型,同时参数只能是DISPATCH_QUEUE_CONCURRENT
(5)dispatch_once
用处也很多,它会让我们的某个操作在生命周期中只执行一次,常用在单例模式中
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 执行一次
});
(6)dispatch_after
如果我们需要做一些延时操作是可以通过dispatch_after来完成
float delaySecond = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// code to be executed on the main queue after delay
});
(7)GCD定时器,不受runloop影响,虽然稍微复杂点,但是效率还是很高的
// 获得队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 创建一个定时器,这里的定时器(dispatch_source_t类型)其实是个OC对象,所以必须强引用
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 设置定时器的各种属性
// GCD的时间参数,一般是纳秒,NSEC_PER_SEC=10的9次方纳秒
//何时开始执行第一个任务,比当前时间晚1秒,
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);//每隔多长时间执行一次
dispatch_source_set_timer(self.timer, start, interval, 0);
// 设置回调
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"------------%@", [NSThread currentThread]);
count++;
// 取消定时器
dispatch_cancel(self.timer);
self.timer = nil;
});
// 启动定时器
dispatch_resume(self.timer);
小结:
dispatch_async+dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL);开启一条子线程
dispatch_async+dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);系统决定
dispatch_async+dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);系统决定
dispatch_async+dispatch_get_main_queue();不会开启子线程
dispatch_sync+dispatch_get_main_queue();线程死锁
dispatch_sync+其他;都不会开启子线程
如果线程之间有依赖关系,可以通过栈栏函数或者dispatch_queue_create(0, DISPATCH_QUEUE_SERIAL)来完成。
如果需要监听子线程,可以通过dispatch_group_t来完成。
如有不足的地方请大家指正。
网友评论