iOS GCD相关使用方法

1.GCD简介

Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。在 Mac OS X 10.6 雪豹中首次推出,也可在 iOS 4 及以上版本使用。

GCD优点

  • GCD可用于多核的并行运算
  • GCD会利用更多的CPU内核
  • GCD会自动管理线程的生命周期

2.GCD任务和队列

同步执行(sync)

  • 同步添加到指定的队列中,再添加的任务执行结束前,一直等待,在队列里的任务执行完成后在执行
  • 不具备开启线程的能力,在当前线程中执行。

异步执行

  • 异步任务添加到队列中,不会等待,可以继续执行。
  • 可以下新的线程中执行任务。

队列: 用来存放任务的队列。队列是一种特殊的线性表,采用FIFO(先进先出)的原则。

  • 串行队列:每次只有一个任务会被执行,当一个任务执行完成后在执行下一个任务
  • 并发队列:可以让多个任务同时执行。可以开启多个线程,同时执行任务。

3.创建队列

  • 串行队列
     /*
     *串行队列
     *参数1:线程名称可以为空测试时可用
     *参数2:串行队列
     */
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
  • 并发队列
 /*
     *并发队列
     *参数1:线程名称可以为空测试时可用
     *参数2:并发队列
     */
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
  • 对于串行队列,GCD提供了一种特殊的串行队列主队列(Main Dispatch Queue)
    • 所有放到主队列中的任务,都会放到主线程中执行。
    • dispatch_get_main_queue()获取主队列。
dispatch_queue_t queue = dispatch_get_main_queue();
  • 并发队列,GCD默认提供了全局并发队列
/*
     *参数一 表示线程优先级
     *参数二 0即可
     */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

4.GCD的基本使用

  • 同步执行 + 并发队列:
    不会开启新的线程,在当前线程执行,执行完一个任务后在执行下一个。
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"syncConcurrent---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(queue, ^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1----%@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2----%@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3----%@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"syncConcurrent---end");

//打印信息
2018-12-10 15:21:27.866589+0800 GCD[11895:5222420] currentThread---<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:27.866688+0800 GCD[11895:5222420] syncConcurrent---begin
2018-12-10 15:21:29.867784+0800 GCD[11895:5222420] 1----<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:31.869445+0800 GCD[11895:5222420] 1----<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:33.871222+0800 GCD[11895:5222420] 2----<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:35.872828+0800 GCD[11895:5222420] 2----<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:37.874443+0800 GCD[11895:5222420] 3----<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:39.876046+0800 GCD[11895:5222420] 3----<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:39.876239+0800 GCD[11895:5222420] syncConcurrent---end
  • 异步执行+并发队列:
    会开启新线程,所有任务同步执行。
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"syncConcurrent---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1----%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2----%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3----%@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"syncConcurrent---end");
//打印信息
2018-12-10 15:22:58.596939+0800 GCD[11898:5223017] currentThread---<NSThread: 0x1c4069140>{number = 1, name = main}
2018-12-10 15:22:58.597029+0800 GCD[11898:5223017] syncConcurrent---begin
2018-12-10 15:22:58.597099+0800 GCD[11898:5223017] syncConcurrent---end
2018-12-10 15:23:00.603383+0800 GCD[11898:5223043] 2----<NSThread: 0x1c4075ac0>{number = 4, name = (null)}
2018-12-10 15:23:00.603419+0800 GCD[11898:5223041] 1----<NSThread: 0x1c0467340>{number = 3, name = (null)}
2018-12-10 15:23:00.603645+0800 GCD[11898:5223042] 3----<NSThread: 0x1c407af40>{number = 5, name = (null)}
2018-12-10 15:23:02.606575+0800 GCD[11898:5223042] 3----<NSThread: 0x1c407af40>{number = 5, name = (null)}
2018-12-10 15:23:02.608954+0800 GCD[11898:5223043] 2----<NSThread: 0x1c4075ac0>{number = 4, name = (null)}
2018-12-10 15:23:02.609031+0800 GCD[11898:5223041] 1----<NSThread: 0x1c0467340>{number = 3, name = (null)}
  • 同步执行+串行队列
    不会开启新的线程,在当前线程执行任务,任务是串行,在执行完成后一个任务在执行下一个任务。
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"syncConcurrent---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1----%@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2----%@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3----%@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"syncConcurrent---end");
//打印信息
2018-12-10 15:25:54.043186+0800 GCD[11901:5223884] currentThread---<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:25:54.043275+0800 GCD[11901:5223884] syncConcurrent---begin
2018-12-10 15:25:56.044897+0800 GCD[11901:5223884] 1----<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:25:58.046774+0800 GCD[11901:5223884] 1----<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:26:00.048408+0800 GCD[11901:5223884] 2----<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:26:02.050100+0800 GCD[11901:5223884] 2----<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:26:04.051480+0800 GCD[11901:5223884] 3----<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:26:06.054114+0800 GCD[11901:5223884] 3----<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:26:06.054304+0800 GCD[11901:5223884] syncConcurrent---end
  • 异步执行 + 串行队列
    会开启新的线程,但是任务是串行执行,执行完成一个任务在执行下一个任务。
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"syncConcurrent---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1----%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2----%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i<2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3----%@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"syncConcurrent---end");
//打印信息
2018-12-10 15:27:46.578929+0800 GCD[11904:5224555] currentThread---<NSThread: 0x1c007f840>{number = 1, name = main}
2018-12-10 15:27:46.579018+0800 GCD[11904:5224555] syncConcurrent---begin
2018-12-10 15:27:46.579133+0800 GCD[11904:5224555] syncConcurrent---end
2018-12-10 15:27:48.585620+0800 GCD[11904:5224577] 1----<NSThread: 0x1c0479980>{number = 3, name = (null)}
2018-12-10 15:27:50.590468+0800 GCD[11904:5224577] 1----<NSThread: 0x1c0479980>{number = 3, name = (null)}
2018-12-10 15:27:52.596004+0800 GCD[11904:5224577] 2----<NSThread: 0x1c0479980>{number = 3, name = (null)}
2018-12-10 15:27:54.601597+0800 GCD[11904:5224577] 2----<NSThread: 0x1c0479980>{number = 3, name = (null)}
2018-12-10 15:27:56.607246+0800 GCD[11904:5224577] 3----<NSThread: 0x1c0479980>{number = 3, name = (null)}
2018-12-10 15:27:58.610236+0800 GCD[11904:5224577] 3----<NSThread: 0x1c0479980>{number = 3, name = (null)}
  • 同步执行 + 主队列(在主线程):
    出现死锁,相互等待卡主不执行。syncMain任务放到主线程中队列执行。同步执行会等待当前队列中的任务执行完成后才会执行
  • 异步执行 + 主队列 :
    在主线程中执行任务,执行完成一个任务后再执行下一个任务。

5.GCD信号量

  • 当我们在进行网络请求是,需要在一个请求执行完成后再去执行下一个请求是,因为网络请求是异步执行,需要用到信号量。当信号量为0的时候就会阻塞线程,大于0的时候就不会阻塞线程,通过改变信号量的值就可以控制线程。

  • dispatch_semaphore_create 创建信号量

  • dispatch_semaphore_signal
    1.返回值long类型,当返回值为0时,表示当前没有线程等待处理的信号,信号量增加1。
    2.当返回值不为0时,表示当前有一个会多个线程等待处理的信号量,并且该函数唤醒了一个等待的线程。

  • dispatch_semaphore_wait
    1.等待信号,判断信号量是否大于0,如果大于0就减掉1个信号往下执行。
    2.如果等于0函数就会阻塞当前的线程。

//创建信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    NSLog(@"开始网络请求");
    
    [[SetNetWorking baseHttpManager] upLoadDataImage:image successBlock:^(id response) {
        //信号量+1
        
        NSLog(@"正在网络请求");
        
        dispatch_semaphore_signal(semaphore);
        if (response) {
            
        }else{
            
        }
    } failedBlock:^(NSError * _Nonnull error) {
        dispatch_semaphore_signal(semaphore);
        [ToastView toastWithNSString:@"上传图片失败"];
    }];
    //信号量为0的时候回阻塞线程
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    
    NSLog(@"网络请求完成");

//打印信息

6.GCD 队列组:dispatch_group

  • 创建队列组dispatch_group_t group = dispatch_group_create();
dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"Request_1");
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"Request_2");
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"Request_3");
    });
    //队列组执行完后执行
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"任务均完成,刷新界面");
    });

但是在进行网络请求是,请求都是异步执行,要实现在所有请求执行完成后再执行后面的任务需要用到dispatch_group_enter(group)dispatch_group_leave(group)

  • dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数+1
  • dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数-1
  • 当 group 中未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞,以及执行追加到dispatch_group_notify中的任务

dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_enter(group);
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //请求1
        [网络请求:{
            成功:dispatch_group_leave(group);
            失败:dispatch_group_leave(group);
        }];
    });
    dispatch_group_enter(group);
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [网络请求:{
            成功:dispatch_group_leave(group);
            失败:dispatch_group_leave(group);
        }];
    });
    dispatch_group_enter(group);
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [网络请求:{
            成功:dispatch_group_leave(group);
            失败:dispatch_group_leave(group);
        }];
    });
    //队列组执行完后执行
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"任务均完成,刷新界面");
    });
  • dispatch_group_wait会阻塞当前线程,当组中多有任务完成后再执行下面任务

GCD栅栏

dispatch_barrier_async在异步执行两组操作,需要实现在执行完一组后在执行一组是需要使用到栅栏

7.GCD定时器

__block NSInteger time = 10; //倒计时时间
    
    //创建全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //使用全局队列创建计时器
    dispatch_source_t sourceTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    /*
     *参数一:传入计时器
     *参数二:计时器开始时间
     *参数三:计时器间隔时间
     *参数四:代表精准度,0是最精确
    */
    dispatch_source_set_timer(sourceTimer, DISPATCH_TIME_NOW, 1.0*NSEC_PER_SEC, 0);
    //开始执行计时器任务
    dispatch_source_set_event_handler(sourceTimer, ^{
        if (time<=0) {
            //计时器关闭
            dispatch_source_cancel(sourceTimer);
            NSLog(@"执行完成");
        }else{
            NSLog(@"%ld",time);
        }
        time --;
    });
    
    //开始执行计时器任务
    dispatch_resume(sourceTimer);

8.GCD一次性代码只执行一次。

  • 我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了 GCD 的dispatch_once 函数。使用
    dispatch_once 函数能保证某段代码在程序运行过程中只被执行1次,并且即使在多线程的环境下,dispatch_once也可以保证线程安全。
/**
 * 一次性代码(只执行一次)dispatch_once
 */
- (void)once {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 只执行1次的代码(这里面默认是线程安全的)
    });
}

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

推荐阅读更多精彩内容

  • 文用来介绍 iOS 多线程中 GCD 的相关知识以及使用方法。通过本文,您将了解到: 1. GCD 简介 2. G...
    晓_我想去环游世界阅读 1,142评论 2 8
  • 本文用来介绍 iOS 多线程中 GCD 的相关知识以及使用方法。这大概是史上最详细、清晰的关于 GCD 的详细讲...
    花花世界的孤独行者阅读 500评论 0 1
  • 昨日开怀 今日欢笑 随遇而安也不失调 心有阳光 处处皆美好 上山砍柴 下山歇脚 看炊烟娜娜袅袅 逢河搭桥 冷暖自断...
    海之贝阅读 299评论 0 1
  • 这两天无聊把刘德华主演的一部老电影《五亿探长雷洛传》看了,讲述了一个香港警察从理想青年变成一代枭雄的过程,现记下一...
    一个人的城围阅读 6,217评论 1 0
  • 多年没见,你一点没变。 你文静的坐着 眼睛里的亮光来自远方 我们被人流隔开 他们嗡嗡嗡的发出噪声 我试图去靠近你 ...
    子夜玄白阅读 201评论 2 2