多线程之GCD

GCD术语

  • 串行和并行
  • 同步和异步
  • 关键段
  • 竞争条件
  • 死锁
  • 线程安全
  • 线程上下文切换

平行与并发

  • 平行:多个线程可以同时执行
  • 并发:如果是多核的则多个线程可以同时执行,但是如果是单核的,就会涉及到线程上下文切换,足够快的切换,可以造成一种假象就是在同时执行。

分发队列

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

线性队列

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

并发队列

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

队列类型

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

  • 主队列:dispatch_get_main_queue()
  • 全局队列: dispatch_global_queue(优先级选项)
    优先级:
    • DISPATCH_QUEUE_PRIORITY_HIGH
    • DISPATCH_QUEUE_PRIORITY_LOW
    • DISPATCH_QUEUE_PRIORITY_BACKGROUND
    • DISPATCH_QUEUE_PRIORITY_DEFAULT

diapatch_async

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

不同队列类型的使用指南

  • 自定义的线性队列:当你想线性执行后台任务,并且跟踪此任务的的时候。-dispatch_sync
  • 主队列:更新UI
  • 并发队列:在后台执行非UI工作。

使用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:

  • Main Queue

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

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

为保证单例初始化代码一次只执行一个,可以使用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

  • 自定义的同步队列 custom concurrent queue

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

什么时候适合用dispatch_sync

  • 自定义的同步队列 custom concurrent queue

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_group_wait 这个函数会阻塞你当前的线程直到在组里的所有任务都完成之后。
    相关方法:

    dispatch_group_t downloadGroup = dispatch_group_create(); 创建一个group

    dispatch_group_enter(downloadGroup); 进入

    dispatch_group_leave(downloadGroup); 离开

    dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); 等待

  • dispatch_group_notify 这个函数不会阻塞你当前的线程,在组里的所有任务完成之后,它会异步的通知你。

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

参考文档:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容

  • 1. GCD简介 什么是GCD呢?我们先来看看百度百科的解释简单了解下概念 引自百度百科:Grand Centra...
    千寻_544f阅读 362评论 0 0
  • GCD (Grand Central Dispatch) :iOS4 开始引入,使用更加方便,程序员只需要将任务添...
    池鹏程阅读 1,328评论 0 2
  • 多线程 在iOS开发中为提高程序的运行效率会将比较耗时的操作放在子线程中执行,iOS系统进程默认启动一个主线程,用...
    郭豪豪阅读 2,595评论 0 4
  • OK,既然讲到多线程,那就一次性把全部多线程的内容介绍完毕~ 🔣 简介 什么是GCD 全称是Grand Cent...
    小白文_Vincent阅读 215评论 0 1
  • 柴可夫斯基第六交響曲 悲愴 悲憫自在人心。 長鳴警鐘,不讓麻木環繞。
    咔辣辣阅读 183评论 0 0