iOS多线程编程之Grand Central Dispatch(GCD)

介绍:

Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统。这建立在任务并行执行的线程池模式的基础上的。它首次发布在Mac OS X 10.6 ,iOS 4及以上也可用。

设计:

GCD的工作原理是:让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。

一个任务可以是一个函数(function)或者是一个block。 GCD的底层依然是用线程实现,不过这样可以让程序员不用关注实现的细节。

GCD中的FIFO队列称为dispatch queue,它可以保证先进来的任务先得到执行
dispatch queue分为下面三种:

Serial      

又称为private dispatch queues,同时只执行一个任务。Serial queue通常用于同步访问特定的资源或数据。当你创建多个Serial queue时,虽然它们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的。

Concurrent

又称为global dispatch queue,可以并发地执行多个任务,但是执行完成的顺序是随机的。

Main dispatch queue

它是全局可用的serial queue,它是在应用程序主线程上执行任务的。

我们看看dispatch queue如何使用

1、常用的方法dispatch_async

为了避免界面在处理耗时的操作时卡死,比如读取网络数据,IO,数据库读写等,我们会在另外一个线程中处理这些操作,然后通知主线程更新界面。

用GCD实现这个流程的操作比前面介绍的NSThread  NSOperation的方法都要简单。代码框架结构如下:

  1. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
  2.     // 耗时的操作  
  3.     dispatch_async(dispatch_get_main_queue(), ^{  
  4.         // 更新界面  
  5.     });  
  6. });  
如果这样还不清晰的话,那我们还是用上两篇博客中的下载图片为例子,代码如下:

  1. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
  2.     NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"];  
  3.     NSData * data = [[NSData alloc]initWithContentsOfURL:url];  
  4.     UIImage *image = [[UIImage alloc]initWithData:data];  
  5.     if (data != nil) {  
  6.         dispatch_async(dispatch_get_main_queue(), ^{  
  7.             self.imageView.image = image;  
  8.          });  
  9.     }  
  10. });  

运行显示:


是不是代码比NSThread  NSOperation简洁很多,而且GCD会自动根据任务在多核处理器上分配资源,优化程序。

系统给每一个应用程序提供了三个concurrent dispatch queues。这三个并发调度队列是全局的,它们只有优先级的不同。因为是全局的,我们不需要去创建。我们只需要通过使用函数dispath_get_global_queue去得到队列,如下:

  1. dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    

这里也用到了系统默认就有一个串行队列main_queue

  1. dispatch_queue_t mainQ = dispatch_get_main_queue();    

虽然dispatch queue是引用计数的对象,但是以上两个都是全局的队列,不用retain或release。

2、dispatch_group_async的使用

dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了。下面是一段例子代码:

  1. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
  2. dispatch_group_t group = dispatch_group_create();  
  3. dispatch_group_async(group, queue, ^{  
  4.     [NSThread sleepForTimeInterval:1];  
  5.     NSLog(@"group1");  
  6. });  
  7. dispatch_group_async(group, queue, ^{  
  8.     [NSThread sleepForTimeInterval:2];  
  9.     NSLog(@"group2");  
  10. });  
  11. dispatch_group_async(group, queue, ^{  
  12.     [NSThread sleepForTimeInterval:3];  
  13.     NSLog(@"group3");  
  14. });  
  15. dispatch_group_notify(group, dispatch_get_main_queue(), ^{  
  16.     NSLog(@"updateUi");  
  17. });  
  18. dispatch_release(group);  
dispatch_group_async是异步的方法,运行后可以看到打印结果:

2012-09-25 16:04:16.737 gcdTest[43328:11303] group1
2012-09-25 16:04:17.738 gcdTest[43328:12a1b] group2
2012-09-25 16:04:18.738 gcdTest[43328:13003] group3
2012-09-25 16:04:18.739 gcdTest[43328:f803] updateUi

每个一秒打印一个,当第三个任务执行后,upadteUi被打印。


3、dispatch_barrier_async的使用

dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行

例子代码如下:

  1. dispatch_queue_t queue = dispatch_queue_create("gcdtest.rongfzh.yc", DISPATCH_QUEUE_CONCURRENT);  
  2. dispatch_async(queue, ^{  
  3.     [NSThread sleepForTimeInterval:2];  
  4.     NSLog(@"dispatch_async1");  
  5. });  
  6. dispatch_async(queue, ^{  
  7.     [NSThread sleepForTimeInterval:4];  
  8.     NSLog(@"dispatch_async2");  
  9. });  
  10. dispatch_barrier_async(queue, ^{  
  11.     NSLog(@"dispatch_barrier_async");  
  12.     [NSThread sleepForTimeInterval:4];  
  13.   
  14. });  
  15. dispatch_async(queue, ^{  
  16.     [NSThread sleepForTimeInterval:1];  
  17.     NSLog(@"dispatch_async3");  
  18. });  

打印结果:

2012-09-25 16:20:33.967 gcdTest[45547:11203] dispatch_async1

2012-09-25 16:20:35.967 gcdTest[45547:11303] dispatch_async2

2012-09-25 16:20:35.967 gcdTest[45547:11303] dispatch_barrier_async

2012-09-25 16:20:40.970 gcdTest[45547:11303] dispatch_async3

请注意执行的时间,可以看到执行的顺序如上所述。

4、dispatch_apply 

执行某个代码片段N次。
dispatch_apply(5, globalQ, ^(size_t index) {
    // 执行5次
});

本篇使用的到的例子代码:http://download.csdn.net/detail/totogo2010/4596471

GCD还有很多其他用法,可以参考官方文档

参考的文档还有:http://en.wikipedia.org/wiki/Grand_Central_Dispatch

前两篇多线程博文:iOS多线程编程之NSThread的使用

    iOS多线程编程之NSOperation和NSOperationQueue的使用

著作权声明:本文由http://blog.csdn.net/totogo2010/原创,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!


容芳志 (http://www.cnblogs.com/stoic/)

本文遵循“署名-非商业用途-保持一致”创作公用协议



 

[iOS]GCD小结

0. Brief Introduction

     GCD,全称Grand Central Dispath,是苹果开发的一种支持并行操作的机制。它的主要部件是一个FIFO队列和一个线程池,前者用来添加任务,后者用来执行任务。

     GCD中的FIFO队列称为dispatch queue,它可以保证先进来的任务先得到执行(但不保证一定先执行结束)。

     通过与线程池的配合,dispatch queue分为下面两种:

  •      Serial Dispatch Queue -- 线程池只提供一个线程用来执行任务,所以后一个任务必须等到前一个任务执行结束才能开始。
  •      Concurrent Dispatch Queue -- 线程池提供多个线程来执行任务,所以可以按序启动多个任务并发执行。

1. Basic Management

     我们可以通过dispatch_queue_cretae来创建队列,然后用dispatch_release释放。比如下面两段代码分别创建串行队列和并行队列:

[cpp]  view plain copy
  1. dispatch_queue_t serialQ = dispatch_queue_create("eg.gcd.SerialQueue", DISPATCH_QUEUE_SERIAL);  
  2. dispatch_async(serialQ, ^{  
  3.     // Code here  
  4. });  
  5. dispatch_release(serialQ);  
  6.   
  7. dispatch_queue_t concurrentQ = dispatch_queue_create("eg.gcd.ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);  
  8. dispatch_async(concurrentQ, ^{  
  9.     // Code here  
  10. });  
  11. dispatch_release(concurrentQ);  

     而系统默认就有一个串行队列main_queue和并行队列global_queue:

[cpp]  view plain copy
  1. dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
  2. dispatch_queue_t mainQ = dispatch_get_main_queue();  

     通常,我们可以在global_queue中做一些long-running的任务,完成后在main_queue中更新UI,避免UI阻塞,无法响应用户操作:

[cpp]  view plain copy
  1. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
  2.     // long-running task  
  3.     dispatch_async(dispatch_get_main_queue(), ^{  
  4.         // update UI  
  5.     });  
  6. });  

     上面提到dispatch_async这个接口,用来提交blcok给指定queue进行异步执行。这个接口会在成功提交block后立即返回,然后继续执行下去。由于block是定义在栈上的,所以需要将其复制到堆上,见这里

     与之相对应的是dispatch_sync接口,提交block以供同步执行。这个接口会等到block执行结束才返回,所以不需要复制block。So,如果在调用该接口在当前queue上指派任务,就会导致deadlock。维基百科上给了段示例代码:

[cpp]  view plain copy
  1. dispatch_queue_t exampleQueue = dispatch_queue_create("com.example.unique.identifier", NULL );  
  2. dispatch_sync( exampleQueue,^{  
  3.   dispatch_sync( exampleQueue,^{  
  4.     printf("I am now deadlocked...\n");  
  5.   });});  
  6. dispatch_release( exampleQueue );  
     如果追求的是并发,那么dispatch_sync有什么用呢?关于dispatch_sync的用途,SO上有 讨论

     

2. Normal Control

  • dispatch_once

     如果没有记错的话,在iOS Con 2012上,大众点评的同学分享了个Topic叫《iOS开发最佳实践》,开篇讲singleton实现的演进(怎么演进都有可以挑的刺),后面转折说要把精力放到用户看得到的地方。

     如果把singleton和best practice放在一起,那么我很容易联想到dispatch_once这个函数,它可以保证整个应用程序生命周期中某段代码只被执行一次

[cpp]  view plain copy
  1. static dispatch_once_t onceToken;  
  2. dispatch_once(&onceToken, ^{  
  3.     // code to be executed once  
  4. });  

  • dispatch_after

     有时候我们需要等个几秒钟然后做个动画或者给个提示,这时候可以用dispatch_after这个函数:

[cpp]  view plain copy
  1. double delayInSeconds = 2.0;  
  2. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);  
  3. dispatch_after(popTime, dispatch_get_main_queue(), ^(void){  
  4.     // code to be executed on the main queue after delay  
  5. });  

  • dispatch_set_target_queue

     通过dispatch_set_target_queue函数可以设置一个dispatch queue的优先级,或者指定一个dispatch source相应的事件处理提交到哪个queue上。

[cpp]  view plain copy
  1. dispatch_set_target_queue(serialQ, globalQ);  

  • dispatch_apply

     执行某个代码片段若干次。

[cpp]  view plain copy
  1. dispatch_apply(10, globalQ, ^(size_t index) {  
  2.     // do sth. 10 times  
  3. });  

  • dispatch group

     Dispatch Group机制允许我们监听一组任务是否完成:

[cpp]  view plain copy
  1. dispatch_group_t group = dispatch_group_create();  
  2. dispatch_group_async(group, concurrentQ, blk0);  
  3. dispatch_group_async(group, concurrentQ, blk1);  
  4. dispatch_group_async(group, concurrentQ, blk2);  
  5. dispatch_group_notify(group, mainQ, ^{  
  6.     // update UI  
  7. });  
  8. dispatch_release(group);  

     或者说同步地等待一段时间看是否结束:

[cpp]  view plain copy
  1. dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);  
  2. dispatch_group_wait(group, time);  

  • dispatch_barrier_async

     通过dispatch_barrier_async函数提交的任务会等它前面的任务执行结束才开始,然后它后面的任务必须等它执行完毕才能开始。

[cpp]  view plain copy
  1. dispatch_async(concurrentQ, blk0);  
  2. dispatch_async(concurrentQ, blk1);  
  3. dispatch_barrier_async(concurrentQ, blk_barrier);  
  4. dispatch_async(concurrentQ, blk2);  

这份官方文档很清晰地按功能为GCD相关函数进行了分类。

Jason Lee @ Hangzhou

原文链接:http://blog.csdn.net/jasonblog/article/details/7816999


以下是对提供的参考资料的总结,按照要求结构化多个要点分条输出: 4G/5G无线网络优化与网规案例分析: NSA站点下终端掉4G问题:部分用户反馈NSA终端频繁掉4G,主要因终端主动发起SCGfail导致。分析显示,在信号较好的环境下,终端可能因节能、过热保护等原因主动释放连接。解决方案建议终端侧进行分析处理,尝试关闭节电开关等。 RSSI算法识别天馈遮挡:通过计算RSSI平均值及差值识别天馈遮挡,差值大于3dB则认定有遮挡。不同设备分组规则不同,如64T和32T。此方法可有效帮助现场人员识别因环境变化引起的网络问题。 5G 160M组网小区CA不生效:某5G站点开启100M+60M CA功能后,测试发现UE无法正常使用CA功能。问题原因在于CA频点集标识配置错误,修正后测试正常。 5G网络优化与策略: CCE映射方式优化:针对诺基亚站点覆盖农村区域,通过优化CCE资源映射方式(交织、非交织),提升RRC连接建立成功率和无线接通率。非交织方式相比交织方式有显著提升。 5G AAU两扇区组网:与三扇区组网相比,AAU两扇区组网在RSRP、SINR、下载速率和上传速率上表现不同,需根据具体场景选择适合的组网方式。 5G语音解决方案:包括沿用4G语音解决方案、EPS Fallback方案和VoNR方案。不同方案适用于不同的5G组网策略,如NSA和SA,并影响语音连续性和网络覆盖。 4G网络优化与资源利用: 4G室分设备利旧:面对4G网络投资压减与资源需求矛盾,提出利旧多维度调优策略,包括资源整合、统筹调配既有资源,以满足新增需求和提质增效。 宏站RRU设备1托N射灯:针对5G深度覆盖需求,研究使用宏站AAU结合1托N射灯方案,快速便捷地开通5G站点,提升深度覆盖能力。 基站与流程管理: 爱立信LTE基站邻区添加流程:未提供具体内容,但通常涉及邻区规划、参数配置、测试验证等步骤,以确保基站间顺畅切换和覆盖连续性。 网络规划与策略: 新高铁跨海大桥覆盖方案试点:虽未提供详细内容,但可推测涉及高铁跨海大桥区域的4G/5G网络覆盖规划,需考虑信号穿透、移动性管理、网络容量等因素。 总结: 提供的参考资料涵盖了4G/5G无线网络优化、网规案例分析、网络优化策略、资源利用、基站管理等多个方面。 通过具体案例分析,展示了无线网络优化中的常见问题及解决方案,如NSA终端掉4G、RSSI识别天馈遮挡、CA不生效等。 强调了5G网络优化与策略的重要性,包括CCE映射方式优化、5G语音解决方案、AAU扇区组网选择等。 提出了4G网络优化与资源利用的策略,如室分设备利旧、宏站RRU设备1托N射灯等。 基站与流程管理方面,提到了爱立信LTE基站邻区添加流程,但未给出具体细节。 新高铁跨海大桥覆盖方案试点展示了特殊场景下的网络规划需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值