GCD 总结

本文是对GCD的全面总结,详细介绍了其方法的应用场景,包括dispatch_async, dispatch_after, dispatch_once, Dispatchbarriers, dispatch_sync等核心概念,并提供了实例代码以加深理解。此外,还讨论了dispatch_group在任务管理上的应用,以及dispatch_apply在并发循环中的使用。
摘要由CSDN通过智能技术生成

GCD 总结


通过前面两篇文章和联系,应该学到了多线程和GCD的知识。

但是没有不断的使用和练习,我们永远也不能灵活使用。


这一篇文章就是对GCD的总结,概念是不会出现在文章中。

文章只是简单总结了GCD的一些方法的运用和什么时候运用。


dispatch_async

dispatch_async 是最简单也是最基础的GCD使用

它将开启新的线程来运行block中的代码,并且返回我们需要的值。

例:

[objc]  view plain copy
  1. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // 1    
  2.         UIImage *overlayImage = [self faceOverlayImageFromImage:_image];    
  3.         dispatch_async(dispatch_get_main_queue(), ^{ // 2    
  4.             [self fadeInNewImage:overlayImage]; // 3    
  5.         });    
  6.     });  


使用时间:

1. 自定义串行队列:当你想串行执行后台任务并追踪它时就是一个好选择。这消除了资源争用,因为你知道一次只有一个任务在执行。注意若你需要来自某个方法的数据,你必须内联另一个 Block 来找回它或考虑使用 dispatch_sync。
2. 主队列(串行):这是在一个并发队列上完成任务后更新 UI 的共同选择。要这样做,你将在一个 Block 内部编写另一个 Block 。以及,如果你在主队列调用 dispatch_async 到主队列,你能确保这个新任务将在当前方法完成后的某个时间执行。
3. 并发队列:这是在后台执行非 UI 工作的共同选择。

dispatch_after

dispatch_after 用来延后我们将要进行的操作

例:

[objc]  view plain copy
  1. double delayInSeconds = 1.0;     
  2.     dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); // 1      
  3.     dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // 2      
  4.         if (!count) {     
  5.             [self.navigationItem setPrompt:@"Add photos with faces to Googlyify them!"];     
  6.         } else {     
  7.             [self.navigationItem setPrompt:nil];     
  8.         }     
  9.     });     


使用时间:

1. 自定义串行队列:在一个自定义串行队列上使用 dispatch_after 要小心。你最好坚持使用主队列。
2. 主队列(串行):是使用 dispatch_after 的好选择;Xcode 提供了一个不错的自动完成模版。
3. 并发队列:在并发队列上使用 dispatch_after 也要小心;你会这样做就比较罕见。还是在主队列做这些操作吧。

dispatch_once() 

dispatch_once()  永远只会运行一次的线程,用来保护线程安全

例:

[objc]  view plain copy
  1. + (instancetype)sharedManager     
  2. {     
  3.     static PhotoManager *sharedPhotoManager = nil;     
  4.     static dispatch_once_t onceToken;     
  5.     dispatch_once(&onceToken, ^{     
  6.         sharedPhotoManager = [[PhotoManager alloc] init];     
  7.         sharedPhotoManager->_photosArray = [NSMutableArray array];     
  8.     });     
  9.     return sharedPhotoManager;     
  10. }     
那,上述例子就是保证单例类在项目中只会实例化一次。

使用须知:

这只是让访问共享实例线程安全。它绝对没有让类本身线程安全。类中可能还有其它竞态条件,例如任何操纵内部数据的情况。这些需要用其它方式来保证线程安全

Dispatch barriers

Dispatch barriers 是一组函数,在并发队列上工作时扮演一个串行式的瓶颈。使用 GCD 的障碍(barrier)API 确保提交的 Block 在那个特定时间上是指定队列上唯一被执行的条目。这就意味着所有的先于调度障碍提交到队列的条目必能在这个 Block 执行前完成。

例:

[objc]  view plain copy
  1. dispatch_barrier_async(self.concurrentPhotoQueue, ^{ // 2      
  2.            [_photosArray addObject:photo]; // 3     
  3.            dispatch_async(dispatch_get_main_queue(), ^{ // 4     
  4.                [self postContentAddedNotification];      
  5.            });     
  6.        });    

dispatch_sync()

dispatch_sync()同步地提交工作并在返回前等待它完成。使用 dispatch_sync 跟踪你的调度障碍工作,或者当你需要等待操作完成后才能使用 Block 处理过的数据。如果你使用第二种情况做事,你将不时看到一个 __block 变量写在 dispatch_sync 范围之外,以便返回时在 dispatch_sync 使用处理过的对象。

例:

[objc]  view plain copy
  1. __block NSArray *array; // 1     
  2.     dispatch_sync(self.concurrentPhotoQueue, ^{ // 2     
  3.         array = [NSArray arrayWithArray:_photosArray]; // 3     
  4.     });     

注意你需要很小心。想像如果你调用 dispatch_sync 并放在你已运行着的当前队列。这会导致死锁,因为调用会一直等待直到 Block 完成,但 Block 不能完成(它甚至不会开始!),直到当前已经存在的任务完成,而当前任务无法完成!这将迫使你自觉于你正从哪个队列调用——以及你正在传递进入哪个队列。

何时使用:

1. 自定义串行队列:在这个状况下要非常小心!如果你正运行在一个队列并调用 dispatch_sync 放在同一个队列,那你就百分百地创建了一个死锁。
2. 主队列(串行):同上面的理由一样,必须非常小心!这个状况同样有潜在的导致死锁的情况。
3. 并发队列:这才是做同步工作的好选择,不论是通过调度障碍,或者需要等待一个任务完成才能执行进一步处理的情况。


dispatch_sync() 和 dispatch_async() 的区别

dispatch_sync 添加任务到一个队列并等待直到任务完成。dispatch_async 做类似的事情,但不同之处是它不会等待任务的完成,而是立即继续“调用线程”的其它任务。


dispatch_group 

dispatch_group Dispatch Group 会在整个组的任务都完成时通知你。这些任务可以是同步的,也可以是异步的,即便在不同的队列也行。而且在整个组的任务都完成时,Dispatch Group 可以用同步的或者异步的方式通知你。因为要监控的任务在不同队列,那就用一个 dispatch_group_t 的实例来记下这些不同的任务。

例:

[objc]  view plain copy
  1. - (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock   
  2. {   
  3.     // 1   
  4.     __block NSError *error;   
  5.     dispatch_group_t downloadGroup = dispatch_group_create();    
  6.    
  7.     for (NSInteger i = 0; i < 3; i++) {   
  8.         NSURL *url;   
  9.         switch (i) {   
  10.             case 0:   
  11.                 url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];   
  12.                 break;   
  13.             case 1:   
  14.                 url = [NSURL URLWithString:kSuccessKidURLString];   
  15.                 break;   
  16.             case 2:   
  17.                 url = [NSURL URLWithString:kLotsOfFacesURLString];   
  18.                 break;   
  19.             default:   
  20.                 break;   
  21.         }   
  22.    
  23.         dispatch_group_enter(downloadGroup); // 2   
  24.         Photo *photo = [[Photo alloc] initwithURL:url   
  25.                               withCompletionBlock:^(UIImage *image, NSError *_error) {   
  26.                                   if (_error) {   
  27.                                       error = _error;   
  28.                                   }   
  29.                                   dispatch_group_leave(downloadGroup); // 3   
  30.                               }];   
  31.    
  32.         [[PhotoManager sharedManager] addPhoto:photo];   
  33.     }   
  34.    
  35.     dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ // 4   
  36.         if (completionBlock) {   
  37.             completionBlock(error);   
  38.         }   
  39.     });   
  40. }   


何时使用:

1. 自定义串行队列:它很适合当一组任务完成时发出通知。

2. 主队列(串行):它也很适合这样的情况。但如果你要同步地等待所有工作地完成,那你就不应该使用它,因为你不能阻塞主线程。然而,异步模型是一个很有吸引力的能用于在几个较长任务(例如网络调用)完成后更新 UI 的方式。

3. 并发队列:它也很适合 Dispatch Group 和完成时通知。


dispatch_apply 

dispatch_apply  表现得就像一个 for 循环,但它能并发地执行不同的迭代。这个函数是同步的,所以和普通的 for 循环一样,它只会在所有工作都完成后才会返回。

例:

[objc]  view plain copy
  1. dispatch_apply(3, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t i) {   
  2.    
  3.         NSURL *url;   
  4.         switch (i) {   
  5.             case 0:   
  6.                 url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];   
  7.                 break;   
  8.             case 1:   
  9.                 url = [NSURL URLWithString:kSuccessKidURLString];   
  10.                 break;   
  11.             case 2:   
  12.                 url = [NSURL URLWithString:kLotsOfFacesURLString];   
  13.                 break;   
  14.             default:   
  15.                 break;   
  16.         }   
  17.    

何时使用:

1. 自定义串行队列:串行队列会完全抵消 dispatch_apply 的功能;你还不如直接使用普通的 for 循环。

2. 主队列(串行):与上面一样,在串行队列上不适合使用 dispatch_apply 。还是用普通的 for 循环吧。

3. 并发队列:对于并发循环来说是很好选择,特别是当你需要追踪任务的进度时。

 


以上就是对前面三篇文章的一个总结,我将提到的方法都取出来,解释,举例,并简单告知大家该在什么时候使用。

如果你还是不太明白怎么使用,那你还是需要仔细看看前面的文章,然后多联系,多看其他的文章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值