上一篇文章我对GCD的几个基本概念做了介绍,但是大家看完了可能觉得对理解GCD并没有什么卵用。其实会用GCD其实很简单,只要记住两条就可以了。
1. 主队列里的任务必须在异步函数中执行。
主队列里的任务如果在同步函数中执行,就会造成死锁。什么是死锁?容我举例来说明。
代码
- (void)viewDidLoad {
[super viewDidLoad];
// 1.
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"开始执行%@",[NSThread currentThread]);
// 2.
dispatch_sync(queue, ^{
NSLog(@"执行任务%@",[NSThread currentThread]);
});
// 3.
NSLog(@"结束执行%@",[NSThread currentThread]);
}
日志2016-11-04 14:27:44.261 TTTTTTTTTT[12014:139481] 开始执行<NSThread: 0x60000007d140>{number = 1, name = main}
看到没有,主队列里的任务没有执行,程序也没有往下执行,这就叫做死锁。那又为什么会发生死锁。主队列里的任务默认都在主线程中执行,程序都把主队列里的任务放在主线程的最后面。当程序执行到2时,会把任务1放在主线程的后面,等主线程里的任务都执行完了在执行。可是这是什么函数啊?这是同步函数啊,它会堵塞当前的线程,后面的任务会等任务1执行完再执行。所以他们又开始互相等待,永远不会往下执行了。
任务1对其他任务说:你们快执行啊?你们执行完我才能执行!
其他任务对任务1说:不行啊,你执行完我们才能执行。这是同步那个二货规定的。
任务1和其他任务就开始了漫长的等待……
2. 全局并发队列里的任务在异步函数中执行才能实现并发。
代码
// 点击屏幕开始下载图片
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSString *strURL1 = @"http://h.hiphotos.baidu.com/zhidao/pic/item/6d81800a19d8bc3ed69473cb848ba61ea8d34516.jpg";
NSString *strURL2 = @"http://h.hiphotos.baidu.com/zhidao/pic/item/0eb30f2442a7d9334f268ca9a84bd11372f00159.jpg";
NSString *strURL3 = @"http://www.2liangli.com/uploads/allimg/141108/1-14110R12140136.png";
//获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"%@开始下载第一张图片",[NSThread currentThread]);
UIImage *image = [self downloadImageWithURL:strURL1];
// 主线程中更新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView1.image = image;
});
});
dispatch_async(queue, ^{
NSLog(@"%@开始下载第二张图片",[NSThread currentThread]);
UIImage *image = [self downloadImageWithURL:strURL2];
// 主线程中更新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView2.image = image;
});
});
dispatch_async(queue, ^{
NSLog(@"%@开始下载第三张图片",[NSThread currentThread]);
UIImage *image = [self downloadImageWithURL:strURL3];
// 主线程中更新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView3.image = image;
});
});
}
- (UIImage *)downloadImageWithURL : (NSString *)strURL {
NSURL *url = [NSURL URLWithString:strURL];
return [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
}
日志
2016-11-04 14:32:23.532 TTTTTTTTTT[12195:142364] <NSThread: 0x608000261500>{number = 5, name = (null)}开始下载第三张图片
2016-11-04 14:32:23.532 TTTTTTTTTT[12195:142365] <NSThread: 0x60000007fac0>{number = 3, name = (null)}开始下载第一张图片
2016-11-04 14:32:23.532 TTTTTTTTTT[12195:142368] <NSThread: 0x608000261700>{number = 4, name = (null)}开始下载第二张图片
我们发现程序创建了三条线程,每条线程都执行下载图片的任务,三张图片几乎是同时开始下载的。这就实现了并发,也没有堵塞主线程。最重要的一点,下载完的图片,一定要在主线程更新。
如果有兴趣可以试试把并发队列里的任务放在同步函数中执行,我们就会发现三张图片的下载都是在主线程中进行的,图片好长时间才下载完。因为图片是一个一个下载的,而不像上面是同时开始下载。
至于自定义队列,我就不讲了,因为和上面的原理差不多,而且用的地方也不多。并发队列系统已经提供了,我们就不要自己创建了。至于串行队列,我们运用多线程就是为了多任务同时进行,串行队列里的任务无论在同步函数还是异步函数中,都不可能同时进行。
关于GCD还有很多其他的用处,我会在下一篇文章中讲!