IOSIOS面试裤iOS

多线程之GCD

2015-03-26  本文已影响208人  wright

GCD术语

平行与并发

分发队列

GCD提供了分发队列去处理代码块,这些任务会以先进先出的方式执行。所有的分发队列自己是线程安全的,你可以同时从不同的线程去获取他们。下面会介绍两种GCD提供的特殊队列:

线性队列

在线性队列中的任务一次只执行一个,每个任务只在上个任务执行完成后开始执行,你不能确保每个代码块执行的时间,但是这些任务会按照添加到队列中的顺序进行执行。在一个线性队列的两个任务不可能同时执行。

并发队列

并发队列中的任务会以添加到队列中的顺序进行执行,但是你不能确保他们以何种顺序结束。所以你无法确保同一时间有多少个任务正在执行。

队列类型

首先,系统提供了一个叫做“main queue”的特殊线性队列,只可以在这个线程中更新你的UI,这个队列用来向UIViews发送消息或者发布通知。系统也提供了几个并发队列-“Global Dispatch Queues”,它们是四种拥有不同优先级的全局队列,优先级为“background, low, default, high”。应该意识到Appple's API也在使用这些队列,所以你添加到这些队列的任何任务不是这些队列中的唯一任务。 最后,你可以创建你自己的线性或者并发队列。这意味着你至少有五种队列:主队列,四种全局分发队列,加之你可以自定义的队列。

diapatch_async

dispatch_async在一个队列中增加一个block并且立即返回。在block中的task将在之后特定的时间被执行。在执行基于网络的或者cpu密集型任务的时候应该使用dispatch_async在后台执行。

不同队列类型的使用指南

使用dispatch_after延迟工作

在延迟的一段时间后再执行。

double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_MSEC));

dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    if (!count) {
        [self.navigationItem setPrompt:@"Add photos with faces to Googlyiy them!"];
    } else {
        [self.navigationItem setPrompt:nil];
    }
});

什么时候适合用dispatch_after:

在使用单例模式时要注意线程安全

单例经常在同一时间被多个控制器使用。

为保证单例初始化代码一次只执行一个,可以使用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_once()以线程安全的方式一次只执行一个block。
注:以上的代码只是保证以线程安全的方式获取共享实例,没有使类线程安全。

读者写者问题

许多可变的对象,如NSMutableArray,不能保证在一个线程读的时候,其他线程不能写。
GCD提供了一种优雅的解决方式:读写锁 dispatch barriers

读者:

- (void)addPhoto:(Photo *)photo
{
    if (photo) { // 1
        dispatch_barrier_async(self.concurrentPhotoQueue, ^{ // 2 
            [_photosArray addObject:photo]; // 3
            dispatch_async(dispatch_get_main_queue(), ^{ // 4
                [self postContentAddedNotification]; 
            });
        });
    }
}

什么时候适合用dispatch barriers

写者:为确保读者的线程安全,写者要和读者在同一个分发队列中,使用dispatch_sync去等待读完成。

什么时候适合用dispatch_sync

eg:

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

Dispatch Groups

Dispatch groups在一组task全部完成之后通知你。这些任务可以是同步或者异步的甚至在不同队列中。

在组里的所有事件完成的时候,GCD API提供了两种两种方式进行通知。

Dispatch_apply

dispatch_apply扮演了for循环的角色,它会同时执行循环中的各个条件。

什么时候适合用dispatch_apply

eg:

  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;
    }

    dispatch_group_enter(downloadGroup);
    Photo *photo = [[Photo alloc] initwithURL:url
                          withCompletionBlock:^(UIImage *image, NSError *_error) {
                              if (_error) {
                                  error = _error;
                              }
                              dispatch_group_leave(downloadGroup);
                          }];

    [[PhotoManager sharedManager] addPhoto:photo];
});     

参考文档:

上一篇 下一篇

猜你喜欢

热点阅读