GCD是苹果公司为多核的并行运算提供的解决方案,也是当前iOS提供的多线程方案中最有魅力的。它会自动合理的利用更多的cpu内核,更重要的是它会自动管理线程的生命周期,不需要我们管理,我们只需要定义想要执行的任务就可以。
一 .队列,线程和任务的关系
1.队列:是管理线程的,相当于线程池,能管理线程什么时候执行。队列分为两种:串行队列和并发队列。
放到串行队列中的任务,GCD 会按FIFO(先进先出)的一取出一个执行一个,一次只能‘调度’一个任务。
放到并发队列中的任务,GCD也会FIFO的取出来,但不同的是,它取出来的一个会放到别的线程,然后再取出来一个放到另一个线程。由于取的动作很快,看起来所有的任务都是在一起执行的。
2.任务:线程里面要执行的操作,在GCD当中就是一个block,所以添加任务很方便。任务有两种执行方式:同步和异步。
如果是同步(sync)操作,将不会开启线程,它会阻塞当前线程,并且等待Block中的任务执行完毕,然后当前线程才会继续往下执行。
如果是异步(async)操作,将开辟线程执行block任务,当前线程会继续往下执行,不会阻塞当前线程。
需要注意的是,在 iOS 7.0以及之前,GCD通常只会开启5-6条线程,在iOS 8.0之后,GCD能够开启非常多的线程。
二 .队列的分类测试
1.主队列:是一个特殊的串行队列,一般来说耗时的任务都要放到别的线程执行,任何需要刷新UI的工作都要在主队列中执行。
测试1:主队列,异步执行
-(void)async_text{ dispatch_queue_t queue = dispatch_get_main_queue(); for (int i = 0; i < 10; ++i) { dispatch_async(queue, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); NSLog(@"---> %d", i); } NSLog(@“happy new year”); [NSThread sleepForTimeInterval:1]; NSLog(@"over"); }
|
结论:虽然不会开启新的线程,但是他会把异步任务降低优先级,在主线程空闲时,才会调度队列中的任务在主线程中执行。
测试2:主队列,同步执行
-(void)sync_text{ dispatch_queue_t q = dispatch_get_main_queue(); NSLog(@"!!!"); dispatch_sync(q, ^{ NSLog(@"%@", [NSThread currentThread]); }); NSLog(@“happy new year”); [NSThread sleepForTimeInterval:1]; NSLog(@"over"); }
|
结论:造成死锁。原因:主队列是串行队列,里面的线程是有顺序的,先执行完一个线程才执行下一个线程,而主队列始终就只有一个主线程,主线程是不会执行完毕的,因为他是无限循环的,除非关闭应用程序。因此在主线程开启一个同步任务,同步任务会想抢占执行的资源,而主线程任务一直在执行某些操作,不肯放手。两个的优先级都很高,最终导致死锁,阻塞线程了。
2. 串行队列
测试1:串行队列,同步执行
- (void)sync{ dispatch_queue_t queue = dispatch_queue_create("com.cobub.queue", DISPATCH_QUEUE_SERIAL); for (int i = 0; i < 10; ++i) { NSLog(@"--- %d", i); dispatch_sync(queue, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); } NSLog(@“over”); }
|
结论:并没有开线程,按顺序一个一个执行完之后才执行最后一个NSLog。
测试2:串行队列,异步执行
- (void)async{ dispatch_queue_t q = dispatch_queue_create(“com.cobub.www”, DISPATCH_QUEUE_SERIAL); for (int i = 0; i < 10; ++i) { NSLog(@"--- %@ %d", [NSThread currentThread], i);
dispatch_async(q, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); } NSLog(@“over”); }
|
结论:开辟一条线程,并且按照串行队列方式去执行。
3.并发队列
测试1: 并发队列,同步执行
-(void)text1{ dispatch_queue_t q = dispatch_queue_create(“com.cobub.www”, DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; ++i) { dispatch_sync(q, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); NSLog(@"---> %i", i); } NSLog(@“over”); }
|
结论:同步不具有创建新线程的能力,并不会开辟新线程去执行任务,会在当前主线程中有顺序的执行。
测试2 : 并发队列,异步执行
-(void)text2{ dispatch_queue_t q = dispatch_queue_create(“Cobub”, DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; ++i) { dispatch_async(q, ^{
NSLog(@"%@ - %d", [NSThread currentThread], i); }); } NSLog(@“over”); }
|
结论:会创建多个线程,操作无序执行,且不影响主线程的执行。
3.全局队列:这是系统提供的一个并发队列,没有名称。
测试1: 全局队列,异步任务
-(void)text3{ dispatch_queue_t queue = dispatch_get_global_queue(0, 0); for (int i = 0; i < 10; ++i) { dispatch_async(queue, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); } NSLog(@“cobub”); } |
结论:运行效果与并发队列相同。
三 .几种嵌套死锁情况
在主队列中同步执行会造成死锁的情况上面已经讲过。下面介绍其它几种情况:
1. 串行队列中开启同步任务后嵌套同步任务
- (void)serial_text1 { dispatch_queue_t queue = dispatch_queue_create(“com.cobub.www”, DISPATCH_QUEUE_SERIAL); NSLog(@"之前-%@",[NSThread currentThread]); dispatch_sync(queue, ^{ NSLog(@“sync之前-%@“,[NSThread currentThread]); // 下面开启同步造成死锁:因为串行队列中线程是有执行顺序的,需要等上面开启的同步任务执行完毕,才会执行下面开启的同步任务。而上面的同步任务还没执行完,要到下面的大括号才算执行完毕,而下面的同步任务已经在抢占资源了,就会发生死锁。 dispatch_sync(queue, ^{ NSLog(@“sync-%@“, [NSThread currentThread]); }); NSLog(@"sync之后-%@",[NSThread currentThread]); }); NSLog(@“之后-%@“, [NSThread currentThread]); } |
2. 串行队列中开启异步任务后嵌套同步任务
-(void)serial_text2 { dispatch_queue_t queue = dispatch_queue_create(“Cobub”, DISPATCH_QUEUE_SERIAL); NSLog(@"之前-%@",[NSThread currentThread]); dispatch_async(queue, ^{ NSLog(@"sync之前-%@", [NSThread currentThread]); dispatch_sync(queue, ^{ NSLog(@"sync-%@",[NSThread currentThread]); }); NSLog(@"sync之后-%@",[NSThread currentThread]); }); NSLog(@"之后 -%@",[NSThread currentThread]); } |
按步骤分析:1.创建串行队列 2.打印 “之前-%@ ” 3.async异步操作,所以不会阻塞当前线程,于是有了两条线程,一条继续往下打印“ 之后-%@”,另一条执行block内容打印“sync之前-%@”。因为两条是并行的,所以打印先后顺序不一定。4.sync同步执行,于是它所在的线程被阻塞,一直等到sync里面的任务完成才会继续往下。于是sync将block任务放在queue中,但是queue是一个串行队列,一次执行一个任务,所以sync的block必须等到前一个任务执行完毕,但是queue正在执行的任务就是被sync阻塞的那个,于是发生了死锁。剩下的两句不会打印。
其实GCD的内容和用法还有很多,本文提供了一些较基础的部分,希望对大家有所帮助。
加入Cobub开发者中心 QQ群:194022996
来和大牛一起讨论技术问题~
Cobub razor是一款开源的移动应用数据统计分析运营平台,它通过对用户行为数据的采集,帮助企业更了解用户,实现数据驱动决策!从而优化产品功能,提升用户体验;实现精细化运营,降低运营成本,提高用户留存率,转化率以及活跃度!
长按识别二维码关注 Cobub开发者中心 订阅号