iOS GCD案例分析和原理总结

GCD采用了C语言语法,通过代码块回调的方式,通过对队列的操作创建线程,功能比较强大。GCD里面的编程主要有四种搭配:串行+异步,串行同步,并发+异步,并发+同步 。

GCD采用了队列的方式对需要创建的线程进行管理,由队列对负责对线程进行调度。系统存在着两个原有的队列:

  • 串行主队列
    可以通过 dispatch_get_main_queue()直接获取,
  • 并行队列dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);方法获取。

***线程的操作是为了执行某些任务,在GCD中,把任务交给合适的队列和确定执行方式,系统就会执行需要的任务. ***

使用情况汇总

  • 串行主队列

    • 主队列同步任务:死锁
NSLog(@"111111");  
dispatch_sync(dispatch_get_main_queue(), ^{  
      
    NSLog(@"22222");  
});  
NSLog(@"333333");  

dispatch_sync 表示同步的方式执行一个任务,由主队列去调度。上面的代码会出现死锁现象,只会输出11111,后面的不会输出。因为主队列是串行队列,里面的线程执是有先后顺序,先近先出,主队列里面只有一个线程就是主线程,只有当主线程执行完的时候才开启新的任务,但是主线程任务是不会执行完毕的,主线程的当前执行任务和新的任务优先级都比较高,出现了死锁。

  • 主队列异步任务
  NSLog(@"1111 %@",[NSThread currentThread]);  
  dispatch_async(dispatch_get_main_queue(), ^{  
      NSLog(@"2222%@",[NSThread currentThread]);  
  });  
  NSLog(@"3333 %@",[NSThread currentThread]);  
   //结果
  /*1111 <NSThread: 0x79027750>{number = 1, name = main} 
   3333 <NSThread: 0x79027750>{number = 1, name = main} 
   2222<NSThread: 0x79027750>{number = 1, name = main}*/  

主队列异步的开启任务,并没有开启一个新的线程去执行,而是把任务放在了主线程中执行,而且不会阻塞主线程,出现死锁现象。异步开启的任务是在系统不忙的时候进行执行的。所以不应该认为,GCD使用就是要创建新线程,去执行任务的。因为没有创建新的线程,所有任务都是在主线程执行,在串行队列任务是有先后顺序的,因为只有一个线程,休眠了也同样会按照创建任务的顺序执行任务。下面的代码应该不难理解。

NSLog(@"1111 %@",[NSThread currentThread]);  
 dispatch_async(dispatch_get_main_queue(), ^{  
     NSLog(@"33333%@",[NSThread currentThread]);  
     [NSThread sleepForTimeInterval:0.2];  
     dispatch_async(dispatch_get_main_queue(), ^{  
         NSLog(@"5555%@",[NSThread currentThread]);  
     });  
        NSLog(@"44444%@",[NSThread currentThread]);  
 }); 
 [NSThread sleepForTimeInterval:0.2];  
 NSLog(@"2222 %@",[NSThread currentThread]);  
   
 /** 
  [19793:453170] 1111 <NSThread: 0x7a252f70>{number = 1, name = main} 
  [19793:453170] 2222 <NSThread: 0x7a252f70>{number = 1, name = main} 
  [19793:453170] 33333<NSThread: 0x7a252f70>{number = 1, name = main} 
  [19793:453170] 44444<NSThread: 0x7a252f70>{number = 1, name = main} 
  [19793:453170] 5555<NSThread: 0x7a252f70>{number = 1, name = main} 
  */  
  • 自定义串行队列

    除了用系统的main 队列外也可以自己创建自己需要的队列

    • 自定义串行队列+同步
//创建一个串行队列  
   dispatch_queue_t sq = dispatch_queue_create("serial_q1",DISPATCH_QUEUE_SERIAL);  
    NSLog(@"1-->%@",[NSThread currentThread]);  
   dispatch_sync(sq, ^{  
     
       NSLog(@"2-->%@",[NSThread currentThread]);  
   });  
   //[NSThread sleepForTimeInterval:0.2];  
   NSLog(@"3-->%@",[NSThread currentThread]);  
     
   /** 
    [19980:463325] 1--><NSThread: 0x7ae7abf0>{number = 1, name = main} 
    [19980:463325] 2--><NSThread: 0x7ae7abf0>{number = 1, name = main} 
    [19980:463325] 3--><NSThread: 0x7ae7abf0>{number = 1, name = main} 
    */  

这里自己创建了一个串行队列,同样是没有创建新的线程,但是在在主线程开启同步任务没有死锁,因为这里把任务添加到了自己创建的队列里面了。而不是原来主线程所在的主队列。

  • 自定义串行队列+异步
dispatch_async(sq, ^{  
        NSLog(@"1-->%@",[NSThread currentThread]);  
        dispatch_async(sq, ^{  
            [NSThread sleepForTimeInterval:1.0];  
            NSLog(@"2-->%@",[NSThread currentThread]);  
        });  
        dispatch_async(sq, ^{  
            NSLog(@"3-->%@",[NSThread currentThread]);  
        });  
    });  
    //[NSThread sleepForTimeInterval:1.0];  
    dispatch_async(sq, ^{  
        NSLog(@"4-->%@",[NSThread currentThread]);  
    });  
    /** 
      1--><NSThread: 0x7be69d00>{number = 2, name = (null)} 
      4--><NSThread: 0x7be69d00>{number = 2, name = (null)} 
      2--><NSThread: 0x7be69d00>{number = 2, name = (null)} 
      3--><NSThread: 0x7be69d00>{number = 2, name = (null)} 
     */  
>这里使用的是自定义串行队列+异步的方式进行执行任务,可以发现,虽然开启了四个任务,但是串行队列仅仅创建了一个线程去执行,而不是在原来的main线程执行,这里的number =2.串行队列,任务要按照添加的顺序执行,输出的是上面的结果,如果把注释的睡眠取消,输出的是1,2,3,4. 因为这里不再是在主线程执行了,而是新创建了一个线程,注释的部分还是在主线程里面,主线程休眠了,后面的代码无法被接着执行,线程发生了调度,导致不能把输出 4 的任务及时添加到自定义的串行队列,就开始执行新线程任务导致的。
  • 全局并行队列
    • 并行队列+同步
dispatch_queue_t cq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0);  
    dispatch_sync(cq, ^{  
        NSLog(@"1-->%@",[NSThread currentThread]);  
        dispatch_sync(cq, ^{  
            NSLog(@"2-->%@",[NSThread currentThread]);  
        });  
    });  
    [NSThread sleepForTimeInterval:2];  
    dispatch_sync(cq, ^{  
        NSLog(@"3-->%@",[NSThread currentThread]);  
    });  
    /** 
     1--><NSThread: 0x78e6ab40>{number = 1, name = main} 
     2--><NSThread: 0x78e6ab40>{number = 1, name = main} 
     3--><NSThread: 0x78e6ab40>{number = 1, name = main} 
     */  

直接使用系统的全局队列是一个并行队列,开启了同步任务,所以不会创建新的线程,3个任务始终只有一个线程就是主线程在执行。所以这里的任务和串行队列+同步的执行方式是一样的,都是有顺序执行的。

  • 全局队列+异步
dispatch_queue_t cq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0);  
   dispatch_async(cq, ^{  
        NSLog(@"1-->%@",[NSThread currentThread]);  
   });  
   dispatch_async(cq, ^{  
       NSLog(@"2-->%@",[NSThread currentThread]);  
   });  
   dispatch_async(cq, ^{  
       NSLog(@"3-->%@",[NSThread currentThread]);  
   });  
     
   /** 
    1--><NSThread: 0x7b6477f0>{number = 2, name = (null)} 
    3--><NSThread: 0x7b6468f0>{number = 4, name = (null)} 
    2--><NSThread: 0x7c13aa60>{number = 3, name = (null)}      
    */  

全局队列+异步执行任务的时候,是没有先后顺序的,比如这里这次输出的是1,3,2下次再次运行,也许就变了是很可能。而且对每一个任务都开启了一个单独的线程去执行。

```objc

dispatch_queue_t cq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT , 0);
dispatch_async(cq, ^{
NSLog(@"1-->%@",[NSThread currentThread]);
dispatch_async(cq, ^{
NSLog(@"2-->%@",[NSThread currentThread]);
});

});
dispatch_async(cq, ^{
NSLog(@"3-->%@",[NSThread currentThread]);
});

/**
1--><NSThread: 0x7a1477c0>{number = 2, name = (null)}
3--><NSThread: 0x7a14eb20>{number = 3, name = (null)}
2--><NSThread: 0x7a1477c0>{number = 2, name = (null)}
*/

>全局队列加异步执行任务的时候,一个任务开启了一个线程去完成,在上面这里可以看出系统也做了优化,用了同一个线程去执行输出1,2.自定义并行队列和直接使用系统自己带的全局队列没有什么区别如下

* 自定义并行

 *** 同样可以自定义并行队列***
   * 异步
```objc
dispatch_queue_t cq = dispatch_queue_create("my", DISPATCH_QUEUE_CONCURRENT);  
    dispatch_async(cq, ^{  
         NSLog(@"1-->%@",[NSThread currentThread]);  
        dispatch_async(cq, ^{  
            NSLog(@"2-->%@",[NSThread currentThread]);  
        }); 
    });  
    dispatch_async(cq, ^{    
        NSLog(@"3-->%@",[NSThread currentThread]);  
    });  

同步

dispatch_queue_t cq = dispatch_queue_create("my", DISPATCH_QUEUE_CONCURRENT);  
    dispatch_sync(cq, ^{  
          
         NSLog(@"1-->%@",[NSThread currentThread]);  
        dispatch_sync(cq, ^{  
              
            NSLog(@"2-->%@",[NSThread currentThread]);  
        }); 
    });  
    dispatch_sync(cq, ^{  
          
        NSLog(@"3-->%@",[NSThread currentThread]);  
    });  
      
    /** 
      1--><NSThread: 0x7971cb10>{number = 1, name = main} 
      2--><NSThread: 0x7971cb10>{number = 1, name = main} 
      3--><NSThread: 0x7971cb10>{number = 1, name = main} 
     */  

总结

  • 同步函数+并发队列 不会开启新的线程
  • 同步函数+串行队列 不会开启新的线程
  • 异步函数+并发队列 会开启N条线程。
  • 异步函数+串行队列 开启一条线程,如果是主队列不会开启新线程。

在使用过程中,应该将更多的注意力放在对任务的安排上,掌握GCD代码运行的规则,而不是放在线程的创建和管理上了。因为这些都是系统自动的,自己根据规则合理的安排任务让系统去调度就行,NSOperation是对GCD的封装,使用起来更方便,但是GCD的功能更灵活,以上内容是个人过去对GCD学习的记录,错误之处恳请指正,

参考链接

http://www.cnblogs.com/wendingding/p/3806821.html
http://www.cnblogs.com/kenshincui/tag/GCD/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1. GCD简介 什么是GCD呢?我们先来看看百度百科的解释简单了解下概念 引自百度百科:Grand Centra...
    千寻_544f阅读 400评论 0 0
  • 在这篇文章中,我将为你整理一下 iOS 开发中几种多线程方案,以及其使用方法和注意事项。当然也会给出几种多线程的案...
    张战威ican阅读 612评论 0 0
  • 知乎、豆瓣的用户群体可能是太高端了。360、小时代在这里被很多人鄙视。其实,你不用 360,人家 360 压根也没...
    suibian123阅读 238评论 0 0
  • 她收拾了收拾,还是出门了。 昨晚拨通他的电话之前,她告诫自己说除非他真的有事找她,要不然死都不要再见面。 前几天他...
    老周家的小疯子阅读 189评论 0 0
  • 尽量不要变成大老娘们儿 “DAMA”已被收入英文俚语词典,指那种精力充沛、消费力强劲的中老年妇女,她们出现在广场上...
    女人的第三世界阅读 1,093评论 0 1