GCD常用方法

本文主要介绍GCD一些API的使用,也就是注重实站!

一、GCD常用API
  • 1.并发队列
  • 2.串行队列
  • 3.设置自定义队列的优先级(dispatch_set_target_queue)
  • 4.线程组(dispatch_group)
  • 5.栅栏操作(dispatch_barrier_async)
  • 6.队列的暂停(dispatch_suspend)与重启( dispatch_resume)
  • 7.多少时间后执行任务(dispatch_after)
  • 8.一次执行(dispatch_once)
二、GCD一些辅助API
  • 1.dispatch_group_enter(group)、dispatch_group_leave(group)
  • 2.线程组等待(dispatch_group_wait)
  • 3.信号量(dispatch_semaphore_t)
  • 4.快速迭代(dispatch_apply)
一、GCD常用API
1.并发队列(Concurrent Dispatch Queue)

只要有空闲的线程,队列就会调度当前任务,交给线程去执行,不需要考虑前面是都有任务在执行,只要有线程可以利用,队列就会调度任务。

    //创建自定义并行队列
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    //创建全局队列
    dispatch_queue_t globQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

并发功能只有在异步(dispatch_async)函数下才有效,在同步函数下失去了任务调度的并发能力.

2.串行队列(Serial Dispatch Queue):

任务按照顺序被调度,前一个任务不执行完毕,队列不会调度,这样任务,都是一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

// 串行队列的创建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);

// 使用主队列(跟主线程相关联的队列)
// 主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行
dispatch_queue_t queue = dispatch_get_main_queue();
3.设置队列的优先级(dispatch_set_target_queue)

1)设置系统提供并行队列的优先级
第一个参数为要设置优先级的queue,第二个参数是参照物,既将第一个queue的优先级和第二个queue的优先级设置一样。

 /* NSLog(@"执行系统提供的并行队列,这个队列有优先级,可以手动设置");
     #define DISPATCH_QUEUE_PRIORITY_HIGH 2
     #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
     #define DISPATCH_QUEUE_PRIORITY_LOW (-2)
     #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
     */
    
    dispatch_queue_t global_queue_background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    dispatch_async(global_queue_background, ^{
        NSLog(@"我是第四:DISPATCH_QUEUE_PRIORITY_BACKGROUND");
    });
    
    dispatch_queue_t global_queue_low = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    dispatch_async(global_queue_low, ^{
        NSLog(@"我是第三:DISPATCH_QUEUE_PRIORITY_LOW");
    });
    
    dispatch_queue_t global_queue_default = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(global_queue_default, ^{
        NSLog(@"我是第二:DISPATCH_QUEUE_PRIORITY_DEFAULT");
    });
    
    dispatch_queue_t global_queue_High = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_async(global_queue_High, ^{
        NSLog(@"我是第一:DISPATCH_QUEUE_PRIORITY_HIGH");
    });

2)设置自定义队列的优先级(dispatch_set_target_queue)

    dispatch_queue_t concurrentQueueT1 = dispatch_queue_create("com.tiankong.GCDDemo", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t global_queue_low = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    dispatch_set_target_queue(concurrentQueueT1, global_queue_low);
    dispatch_async(concurrentQueueT1, ^{
        NSLog(@"concurrentQueueT1我设置了最低优先级");
    });
    
    dispatch_queue_t concurrentQueueT2 = dispatch_queue_create("com.tiankong.GCDDemo", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t global_queue_High = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_set_target_queue(concurrentQueueT2, global_queue_High);
    dispatch_async(concurrentQueueT2, ^{

        NSLog(@"concurrentQueueT2最先执行,因为把我的优先级设为了最高");
    });
4.线程组(dispatch_group)

在追加到Dispatch Queue中的多个任务处理完毕之后想执行结束处理,这种需求会经常出现。如果只是使用一个Serial Dispatch Queue(串行队列)时,只要将想执行的处理全部追加到该串行队列中并在最后追加结束处理即可,但是在使用Concurrent Queue 时,可能会同时使用多个Dispatch Queue时,源代码就会变得很复杂。
在这种情况下,就可以使用dispatch_group。

/*
     关灯,哥哥学习10分钟、姐姐学习20分钟、我学习15分钟,最后关灯睡觉(下面的例子1s = 1m)
     */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group_t = dispatch_group_create();
    dispatch_group_async(group_t, queue, ^{
        //任务执行
        sleep(10);
        NSLog(@"哥哥学习10分钟");
    });
    
    dispatch_group_async(group_t, queue, ^{
        sleep(20);
        NSLog(@"姐姐学习20分钟");
    });
    
    dispatch_group_async(group_t, queue, ^{
        sleep(15);
        NSLog(@"我学习15分钟");
    });
    
    dispatch_group_notify(group_t, queue, ^{
       //上面的线程全部完成
        NSLog(@"关灯睡觉");
    });
5.栅栏操作(dispatch_barrier_async)
  • 只适于自己创建的队列dispatch_queue_create的队列
  • 并行队列实现高效的数据访问和文件访问
__block int a = 5;
    dispatch_queue_t concurrentQueueT = dispatch_queue_create("com.tiankong.GCDDemo", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrentQueueT, ^{
        NSLog(@"读取第一次a == %d",a);
    });
    dispatch_async(concurrentQueueT, ^{
        NSLog(@"读取第二次a == %d",a);
        
    });
    dispatch_async(concurrentQueueT, ^{
        NSLog(@"读取第三次a == %d",a);
    });
    
//    dispatch_async(concurrentQueueT, ^{//这么写,读取的值不能确定,
//        a = 8;
//        NSLog(@"写入操作a = 8");
//    });
    dispatch_barrier_async(concurrentQueueT, ^{
        a = 8;
        NSLog(@"写入a = 8");
    });
    dispatch_async(concurrentQueueT, ^{
        
        NSLog(@"读取第四次a == %d",a);
    });
    dispatch_async(concurrentQueueT, ^{
        NSLog(@"读取第五次a == %d",a);
    });
    dispatch_async(concurrentQueueT, ^{
        NSLog(@"读取第六次a == %d",a);
    });
6.队列的暂停(dispatch_suspend)与重启( dispatch_resume)
  • 注意:必须成对出现
dispatch_queue_t concurrentQueueT = dispatch_queue_create("com.tiankong.GCDDemo", DISPATCH_QUEUE_CONCURRENT);
   dispatch_async(concurrentQueueT, ^{
       NSLog(@"读取第一次");
   });
   
   dispatch_suspend(concurrentQueueT);
   sleep(10);
   dispatch_resume(concurrentQueueT);
   dispatch_async(concurrentQueueT, ^{
       NSLog(@"读取第二次");
       
   });
7.多少时间后执行任务(dispatch_after)

下面这句dispatch_after的真正含义是在2秒后把任务添加进队列中,并不是表示在2秒后执行,大部分情况该函数能达到我们的预期,只有在对时间要求非常精准的情况下才可能会出现问题。

  • 注意dispatch_after(每10秒会有1秒的误差,10秒面变成11秒,实践所得)
延迟2秒执行
NSTimeInterval  duration = 2.0;
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)((duration)/*延迟执行时间*/ * NSEC_PER_SEC));   
 dispatch_after(delayTime, dispatch_get_main_queue(), ^{
        //执行2秒后的操作
  });
8.一次执行(dispatch_once)
//一次性执行
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
    // some one-time task
});
二、GCD一些辅助API
1.dispatch_group_enter(group)、dispatch_group_leave(group)

个人理解:和内存管理的引用计数类似,我们可以认为group也持有一个整形变量(只是假设),当调用enter时计数加1,调用leave时计数减1,当计数为0时会调用dispatch_group_notify并且dispatch_group_wait会停止等待;

  • 下面的例子你可以去掉dispatch_group_enter(group)、dispatch_group_leave(group)试试,这种情况就是项目中UI需要多个接口返回数据展示的方法
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_group_t group_t = dispatch_group_create();


    dispatch_group_enter(group_t);

    dispatch_group_async(group_t, queue, ^{
        dispatch_async(queue1, ^{
            sleep(3);
            NSLog(@"我学习15分钟");
            dispatch_group_leave(group_t);

        });
        
    });
    
    dispatch_group_notify(group_t, queue, ^{
        //上面的线程全部完成
        NSLog(@"关灯睡觉");
    });
2.线程组等待(dispatch_group_wait)

个人理解:和dispatch_group_notify功能类似(多了一个dispatch_time_t参数可以设置超时时间),在group上任务完成前,dispatch_group_wait会阻塞当前线程(所以不能放在主线程调用)一直等待;当group上任务完成,或者等待时间超过设置的超时时间会结束等待;

/*
     关灯,哥哥学习10分钟、姐姐学习20分钟、我学习15分钟,最后关灯睡觉(下面的例子1s = 1m)
     */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group_t = dispatch_group_create();
    dispatch_group_async(group_t, queue, ^{
        //任务执行
        sleep(10);
        NSLog(@"哥哥学习10分钟");
    });
    
    dispatch_group_async(group_t, queue, ^{
        sleep(20);
        NSLog(@"姐姐学习20分钟");
    });
    
    dispatch_group_async(group_t, queue, ^{
        sleep(15);
        NSLog(@"我学习15分钟");
    });
    
    dispatch_group_notify(group_t, queue, ^{
        //上面的线程全部完成
        NSLog(@"关灯睡觉");
    });

    
    dispatch_async(queue, ^{
        dispatch_group_wait(group_t, dispatch_time(DISPATCH_TIME_NOW, 16 * NSEC_PER_SEC));
        NSLog(@"dispatch_group_wait 结束");

    });
3.信号量(dispatch_semaphore_t)

停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此时来了五辆车,那么就有一辆需要等待。

信号量的值就相当于剩余车位的数目,dispatch_semaphore_wait函数就相当于来了一辆车,dispatch_semaphore_signal

就相当于走了一辆车。停车位的剩余数目在初始化的时候就已经指明了(dispatch_semaphore_create(long value)),

调用一次dispatch_semaphore_signal,剩余的车位就增加一个;调用一次dispatch_semaphore_wait剩余车位就减少一个;

当剩余车位为0时,再来车(即调用dispatch_semaphore_wait)就只能等待。有可能同时有几辆车等待一个停车位。有些车主

没有耐心,给自己设定了一段等待时间,这段时间内等不到停车位就走了,如果等到了就开进去停车。而有些车主就像把车停在这,

所以就一直等下去

-(void)semaphore
{
    NSMutableArray *array = [[NSMutableArray alloc] init];
    dispatch_semaphore_t sem = dispatch_semaphore_create(1);
    for (int i=0; i<10000; i++) {
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [array addObject:[NSNumber numberWithInt:i]];
            NSLog(@"%@\n",[NSThread currentThread]
);
            dispatch_semaphore_signal(sem);
        });
    }
}

4.快速迭代(dispatch_apply)
要比for循环快,for 循环是在主线程运行次数大一定会造成卡顿,dispatch_apply异步的并发的队列,自动开启子线程,因此dispatch_apply要快一些

dispatch_apply(10,dispatch_get_global_queue(0, 0), ^(size_t index) {
    NSLog(@"%zd = %@",index,[NSThread currentThread]);
});
//打印结果
2017-02-16 15:10:38.602 02-掌握-GCD的快速迭代-zwj[1447:102911] 3 = <NSThread: 0x608000268e40>{number = 5, name = (null)}
2017-02-16 15:10:38.602 02-掌握-GCD的快速迭代-zwj[1447:102666] 0 = <NSThread: 0x60000007ee80>{number = 1, name = main}
2017-02-16 15:10:38.602 02-掌握-GCD的快速迭代-zwj[1447:102912] 2 = <NSThread: 0x60000026ca00>{number = 4, name = (null)}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102910] 1 = <NSThread: 0x60800007eac0>{number = 3, name = (null)}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102911] 4 = <NSThread: 0x608000268e40>{number = 5, name = (null)}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102666] 5 = <NSThread: 0x60000007ee80>{number = 1, name = main}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102912] 6 = <NSThread: 0x60000026ca00>{number = 4, name = (null)}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102910] 7 = <NSThread: 0x60800007eac0>{number = 3, name = (null)}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102911] 8 = <NSThread: 0x608000268e40>{number = 5, name = (null)}
2017-02-16 15:10:38.603 02-掌握-GCD的快速迭代-zwj[1447:102666] 9 = <NSThread: 0x60000007ee80>{number = 1, name = main}

最后附上GCDDemo
如果有什么GCD的问题,欢迎留言;如果你对gcd不太熟悉,那么可以点收藏或者关注我哦,方便将来复制操作。

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

推荐阅读更多精彩内容