GCD 总结

GCD 总结


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

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


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

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


dispatch_async

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

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

例:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // 1  
        UIImage *overlayImage = [self faceOverlayImageFromImage:_image];  
        dispatch_async(dispatch_get_main_queue(), ^{ // 2  
            [self fadeInNewImage:overlayImage]; // 3  
        });  
    });


使用时间:

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

dispatch_after

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

例:

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


使用时间:

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

dispatch_once() 

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

例:

+ (instancetype)sharedManager   
{   
    static PhotoManager *sharedPhotoManager = nil;   
    static dispatch_once_t onceToken;   
    dispatch_once(&onceToken, ^{   
        sharedPhotoManager = [[PhotoManager alloc] init];   
        sharedPhotoManager->_photosArray = [NSMutableArray array];   
    });   
    return sharedPhotoManager;   
}   
那,上述例子就是保证单例类在项目中只会实例化一次。

使用须知:

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

Dispatch barriers

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

例:

 dispatch_barrier_async(self.concurrentPhotoQueue, ^{ // 2    
            [_photosArray addObject:photo]; // 3   
            dispatch_async(dispatch_get_main_queue(), ^{ // 4   
                [self postContentAddedNotification];    
            });   
        });  

dispatch_sync()

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

例:

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

注意你需要很小心。想像如果你调用 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 的实例来记下这些不同的任务。

例:

- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock 
{ 
    // 1 
    __block NSError *error; 
    dispatch_group_t downloadGroup = dispatch_group_create();  
 
    for (NSInteger i = 0; i < 3; i++) { 
        NSURL *url; 
        switch (i) { 
            case 0: 
                url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString]; 
                break; 
            case 1: 
                url = [NSURL URLWithString:kSuccessKidURLString]; 
                break; 
            case 2: 
                url = [NSURL URLWithString:kLotsOfFacesURLString]; 
                break; 
            default: 
                break; 
        } 
 
        dispatch_group_enter(downloadGroup); // 2 
        Photo *photo = [[Photo alloc] initwithURL:url 
                              withCompletionBlock:^(UIImage *image, NSError *_error) { 
                                  if (_error) { 
                                      error = _error; 
                                  } 
                                  dispatch_group_leave(downloadGroup); // 3 
                              }]; 
 
        [[PhotoManager sharedManager] addPhoto:photo]; 
    } 
 
    dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ // 4 
        if (completionBlock) { 
            completionBlock(error); 
        } 
    }); 
} 


何时使用:

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

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

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


dispatch_apply 

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

例:

dispatch_apply(3, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t i) { 
 
        NSURL *url; 
        switch (i) { 
            case 0: 
                url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString]; 
                break; 
            case 1: 
                url = [NSURL URLWithString:kSuccessKidURLString]; 
                break; 
            case 2: 
                url = [NSURL URLWithString:kLotsOfFacesURLString]; 
                break; 
            default: 
                break; 
        } 
 

何时使用:

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

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

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

 


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

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






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值