iOS GCD详解一

什么是GCD?

Grand Central Dispatch (GCD) 是异步执行任务之一,即开发者要做的只是定义想执行的任务追加到适当的Dispatch Queue 中。GCD存在于两种Dispatch Queue,一种是要等待上一个执行完,再执行下一个的Serial Dispatch Queue,这叫做串行队列;另一种,则是不需要上一个执行完,就能执行下一个的Concurrent Dispatch Queue,叫做并行队列。这两种,均遵循FIFO(先进先出)原则。

同步与异步

串行与并行针对的是队列,而同步与异步,针对的则是线程。最大的区别在于,同步线程要阻塞当前线程,必须要等待同步线程中的任务执行完,返回以后,才能继续执行下一任务,整个过程是不会创建新线程的;而异步线程则是不用等待,会在新开启的线程中执行任务(执行主队列的任务除外,主队列的任务在主线程中执行)。

GCD的API

一、 dispatch_queue_create

  • 创建一个 Searial Dispatch Queue(串行)

/// 第一个参数为 Serial Dispatch Queue 名称  第二个参数设为 NULL
dispatch_queue_t mySerialDispatchQueue = dispatch_queue_create("com.example.gcd.mySerialDispatchQueue ", NULL);
   ///同步线程
   dispatch_sync(mySerialDispatchQueue, ^{
           //  想执行的任务
   });
   ///异步线程
   dispatch_async(mySerialDispatchQueue, ^{
           //  想执行的任务
   });
  • 创建一个 Concurrent Dispatch Queue(并行)
    /// 第一个参数为 Concurrent Dispatch Queue 名称  第二个参数设为 NULL
    dispatch_queue_t myConcurrent lDispatchQueue = dispatch_queue_create("com.example.gcd.myConcurrent DispatchQueue ", DISPATCH_QUEUE_CONCURRENT);
    ///同步线程
    dispatch_sync(mySerialDispatchQueue, ^{
            //  想执行的任务
    });
    ///异步线程
    dispatch_async(mySerialDispatchQueue, ^{
            //  想执行的任务
    });

二、 Main Dispatch Queue/Global Dispatch Queue

  • Main Dispatch Queue是在主线程中执行的Dispatch Queue,只有一个,所以Main Dispatch Queue是Searial Dispatch Queue(串行队列)
  • Global Dispatch Queue是所有应用程序都可以使用的Concurrent Dispatch Queue(并行队列)
  1. Main Dispatch Queue获取如下:
 dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_async(mainQueue, ^{
         //  想执行的任务
    });

2.Global Dispatch Queue有4个优先级:

  • 高优先级 (Height Priority)
  • 默认优先级 (Default Priority)
  • 低优先级 (Low Priority)
  • 后台优先级 (Background Priority)
各种Dispatch Queue 获取如下:
   /// 高优先级
    dispatch_queue_t globalDisPathQueueHeigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    
    /// 默认优先级
    dispatch_queue_t globalDisPathQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    /// 低优先级
    dispatch_queue_t globalDisPathQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

    /// 后台优先级
    dispatch_queue_t globalDisPathQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

三、 dispatch_set_target_queue

  • dispatch_set_target_queue函数有两个作用:第一,变更队列的执行优先级;第二,目标队列可以成为原队列的执行阶层。
  1. 变更执行优先级
      //优先级变更的串行队列,初始是默认优先级
    dispatch_queue_t serialQueue = dispatch_queue_create("com.gcd.serialQueue", NULL);
            
    //获取优先级为后台优先级的全局队列
    dispatch_queue_t globalDefaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    //变更优先级
                                    
    dispatch_set_target_queue(serialQueue, globalDefaultQueue);

2.设置执行阶层

 //创建目标串行队列
 dispatch_queue_t targetSerialQueue = dispatch_queue_create("com.gcd.targetSerialQueue", DISPATCH_QUEUE_SERIAL);

 //设置执行阶层
 dispatch_set_target_queue(serialQueue1, targetSerialQueue);
 dispatch_set_target_queue(serialQueue2, targetSerialQueue);
 dispatch_set_target_queue(serialQueue3, targetSerialQueue);

   dispatch_async(serialQueue1, ^{
     NSLog(@"1 in");
     [NSThread sleepForTimeInterval:3.f];
     NSLog(@"1 out");
 });
 dispatch_async(serialQueue2, ^{
     NSLog(@"2 in");
     [NSThread sleepForTimeInterval:3.f];
     NSLog(@"2 out");
 });
 dispatch_async(serialQueue3, ^{
     NSLog(@"3 in");
     [NSThread sleepForTimeInterval:3.f];
     NSLog(@"3 out");
 });
打印结果:
2020-03-19 12:49:13.250818+0800 GCDDemo[1602:70311] 1 in
2020-03-19 12:49:16.251712+0800 GCDDemo[1602:70311] 1 out
2020-03-19 12:49:16.252179+0800 GCDDemo[1602:70311] 2 in
2020-03-19 12:49:19.255916+0800 GCDDemo[1602:70311] 2 out
2020-03-19 12:49:19.256236+0800 GCDDemo[1602:70311] 3 in
2020-03-19 12:49:22.261592+0800 GCDDemo[1602:70311] 3 out

四、 dispatch_after

  • dispatch_after: 是一个延迟执行的函数,他有两个参数,第一个参数是dispatch_time_t即延迟多长时间,第二个参数是dispatch_queue_t即添加在哪个队列中
  • dispatch_after添加在某个队列中延迟执行block中的任务,是要等待该队列中的任务执行完才会执行block,也就是如果线程阻塞,延迟执行的时间就不确定了,可能并不是你设置的时长。列如:
 NSLog(@"第一步");
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
         NSLog(@"我是延迟执行的函数 ---%@",[NSThread currentThread]);
     });
     
     NSLog(@"耗时任务开始");
     NSInteger count = 0;
     for (long long i = 0; i < 5000000000; i++)
     {
         count ++;
     }
     NSLog(@"耗时任务结束");
打印结果:
2020-03-19 13:06:13.527401+0800 GCDDemo[1656:78117] 第一步
2020-03-19 13:06:13.527552+0800 GCDDemo[1656:78117] 耗时任务开始
2020-03-19 13:06:25.103525+0800 GCDDemo[1656:78117] 耗时任务结束
2020-03-19 13:06:25.125003+0800 GCDDemo[1656:78117] 我是延迟执行的函数 ---<NSThread: 0x600003a9a0c0>{number = 1, name = main}

通过执行结果我们可以发现,dispatch_after函数block内的打印是在12秒以后才执行,并不是我们设置的5秒,这是因为此时dispatch_after函数的第二个参数传入的是主线程,延迟任务添加在了主线程中,而主线程中有一个耗时的打印任务,要等这个耗时任务执行完才执行dispatch_after的block。

下面我们将dispatch_after函数第二个参数改成另一个队列

  dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
   NSLog(@"第一步");
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{
       NSLog(@"我是延迟执行的函数 ---%@",[NSThread currentThread]);
   });
   
   NSLog(@"耗时任务开始");
   NSInteger count = 0;
   for (long long i = 0; i < 5000000000; i++)
   {
       count ++;
   }
   NSLog(@"耗时任务结束");
打印结果:
2020-03-19 13:10:10.659080+0800 GCDDemo[1694:81120] 第一步
2020-03-19 13:10:10.659228+0800 GCDDemo[1694:81120] 耗时任务开始
2020-03-19 13:10:15.659324+0800 GCDDemo[1694:81249] 我是延迟执行的函数 ---<NSThread: 0x600001146f80>{number = 6, name = (null)}
2020-03-19 13:10:22.447207+0800 GCDDemo[1694:81120] 耗时任务结束

我们发现确实是按照我们设定的5秒执行的

五、 Dispatch Group

  • Dispatch Group: 一个组的概念,可以把相关的任务归并到一个组内来执行,通过监听组内所有任务的执行情况来做相应处理。如下:
   /// 获取一个global线程
   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   /// 创建一个组线程
   dispatch_group_t group = dispatch_group_create();
   
   dispatch_group_async(group, queue, ^{
       NSLog(@"blk0");
   });
   dispatch_group_async(group, queue, ^{
         NSLog(@"blk1");
     });
   dispatch_group_async(group, queue, ^{
         NSLog(@"blk2");
     });
   
   dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       NSLog(@"done");
   });
打印结果:
2020-03-19 17:38:03.313181+0800 GCDDemo[1974:110534] blk0
2020-03-19 17:38:03.313181+0800 GCDDemo[1974:110530] blk1
2020-03-19 17:38:03.313192+0800 GCDDemo[1974:110532] blk2
2020-03-19 17:38:03.328668+0800 GCDDemo[1974:110352] done

所以无论如何添加Dispatch Queue 到Dispatch Group,都可以监听这些处理的结束,其他的方法还有:

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

推荐阅读更多精彩内容