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] 我是延迟执行的函数 ---{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] 我是延迟执行的函数 ---{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: 等待组任务完成,会阻塞当前线程,当任务组执行完毕时,才会解除阻塞当前线程

你可能感兴趣的:(iOS GCD详解一)