iOS底层原理之GCD的任务与队列

1,913 阅读6分钟

GCD

简介

Grand Central Dispatch(GCD)是 Apple 开发的一个多核编程的解决方法。它是一套纯 C 语言的 API,主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。

GCD的优点

  • GCD支持多核并行计算
  • GCD自动管理线程的生命周期(线程的创建、调起、等待、销毁)
  • 使用者只需告知GCD执行任务

任务与队列

任务

任务即执行操作,就是线程中需要执行的代码。在GCD中就是放在block中执行的代码都是任务。任务分为同步任务(sync)异步任务(async)

  • 同步任务(sync):同步添加到队列当中,当前面的任务没有执行完毕时会一直等待,直到前面的任务执行完毕才会执行当前加入的任务。不可以在新的线程中执行,不具备开辟新的线程能力。
  • 异步任务(async):异步添加到队列当中,无需等待前面的任务执行完成才执行,加入队列就会执行。可以在新的线程中执行,具备开辟新的线程能力。注意:具备开启新线程的能力不代表一定开启新的线程

队列

指执行任务的队列,就是等待执行的任务列表,遵循先进先出IFFO原则。在GCD中有串行队列(Serial Dispatch Queue)并行队列(Concurrent Dispatch Queue),又叫并发队列

  • 串行队列(Serial Dispatch Queue):只开启一个线程,每次只有一个任务执行,任务执行完成后才会执行下一个任务。
  • 并行队列(Concurrent Dispatch Queue):可以让多个任务同时执行,可以开启多个线程来执行多个任务。注意:并发队列只有在异步函数下才有效

特殊队列

  • 串行主队列:一种串行的主队列,在主队列中调用不能在主线程中再次调用同步任务,否则会造成堵塞,但是可以在其他线程调用同步任务
  • 全局并发队列:一种全局的并发队列

队列与任务的创建

创建队列

  • 创建串行队列
//第一个参数是队列的名字,自定义;第二个参数表示是串行还是并发
//DISPATCH_QUEUE_SERIAL = NULL,因此DISPATCH_QUEUE_SERIAL可以用NULL代替创建串行队列
dispatch_queue_t queue = dispatch_queue_create("shifx", DISPATCH_QUEUE_SERIAL);
  • 创建并发队列
//第一个参数是队列的名字,自定义;第二个参数表示是串行还是并发
dispatch_queue_t queue = dispatch_queue_create("shifx", DISPATCH_QUEUE_CONCURRENT);

任务的创建

  • 创建同步任务
//创建同步任务(queue是队列)
dispatch_sync(queue, ^{
        
});
  • 创建异步任务
//创建异步任务(queue是队列)
dispatch_async(queue, ^{
        
});

队列与任务总结

综上所述,我们可以得出一共四种组合外加两种特殊的队列,即

  • 串行队列+同步执行
  • 并发队列+同步执行
  • 串行队列+异步执行
  • 并发队列+异步执行
  • 串行主队列
  • 全局并发队列

队列与任务组合使用

串行队列+同步执行

代码实现

-(void)serialSysncText {
    //串行队列
    dispatch_queue_t serial = dispatch_queue_create("shifx", DISPATCH_QUEUE_SERIAL);
    NSLog(@"串行同步任务开始");
    dispatch_sync(serial, ^{
        sleep(1);
        for (int i = 0; i<3; i++) {
            NSLog(@"串行同步任务1 = %d",i);
        }
    });
    dispatch_sync(serial, ^{
        sleep(1);
        for (int i = 0; i<3; i++) {
            NSLog(@"串行同步任务2 = %d",i);
        }
    });
    NSLog(@"串行同步任务结束");
}

运行结果

从运行结果当中,我们可以看到串行队列中的同步任务都是按照先后顺序依次执行的。

并发队列+同步任务

代码实现

-(void)concurrentAsysncText {
    //并发队列
    dispatch_queue_t concurrent = dispatch_queue_create("shifx", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"并发同步任务开始");
    dispatch_sync(concurrent, ^{
        sleep(1);
        for (int i = 0; i<3; i++) {
            NSLog(@"并发同步任务1 = %d",i);
        }
    });
    dispatch_sync(concurrent, ^{
        sleep(1);
        for (int i = 0; i<3; i++) {
            NSLog(@"并发同步任务2 = %d",i);
        }
    });
    NSLog(@"并发同步任务结束");
}

运行结果

从结果得知,并发队列执行的同步任务也是按照顺序依次执行的,因为它们都在主线程中执行的,没有开启新的线程。感兴趣的小伙伴可以打印下线程看下。

串行队列+异步执行

代码实现

-(void)serialAsysncText {
    //串行队列
    dispatch_queue_t serial = dispatch_queue_create("shifx", DISPATCH_QUEUE_SERIAL);
    NSLog(@"串行异步任务开始");
    dispatch_async(serial, ^{
        
        for (int i = 0; i<3; i++) {
            sleep(1);
            NSLog(@"串行异步任务1 = %d",i);
            NSLog(@"当前线程:%@",[NSThread currentThread]);
        }
    });
    dispatch_async(serial, ^{
        for (int i = 0; i<3; i++) {
            sleep(1);
            NSLog(@"串行异步任务2 = %d",i);
            NSLog(@"当前线程:%@",[NSThread currentThread]);
        }
    });
    NSLog(@"串行异步任务结束");
}

运行结果

从运行结果可以看出,

  • 所有的异步任务都是在打印串行异步任务结束后开始执行;
  • 开启了新的线程,但只开启了一个线程;
  • 在串行队列中的异步任务也是按照先后顺序执行。

并发队列+异步执行

代码实现

-(void)concurrentAsysncText {
    //并发队列
    dispatch_queue_t concurrent = dispatch_queue_create("shifx", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"并发异步任务开始");
    dispatch_async(concurrent, ^{
        for (int i = 0; i<3; i++) {
            sleep(1);
            NSLog(@"并发异步任务1 = %d",i);
            NSLog(@"当前线程:%@",[NSThread currentThread]);
        }
    });
    dispatch_async(concurrent, ^{
        
        for (int i = 0; i<3; i++) {
            sleep(1);
            NSLog(@"并发异步任务2 = %d",i);
            NSLog(@"当前线程:%@",[NSThread currentThread]);
        }
    });
    NSLog(@"并发异步任务结束");
}

运行结果

从运行结果来看,

  • 所有的异步任务都是在打印并发异步任务结束后开始执行;
  • 由于两个异步任务,并发队列开启了两个新的线程
  • 两个异步任务执行没有先后顺序

主队列 + 同步任务

在主线程调用主队列同步任务

由于主队列是串行队列,它不能在主线程中调用主队列同步任务,如下所示崩溃原因就是同步任务放在了主线程中,需要等待主线程执行完毕才可以,而主线程需要等待该方法执行完成,两者互相等待造成死锁,所以崩溃了。

其他线程调用主队列同步任务

代码

//使用 NSThread 的 detachNewThreadWithBlock 方法会创建线程
[NSThread detachNewThreadWithBlock:^{    [self mainSysncText];
}];
    
-(void)mainSysncText {
    NSLog(@"当前所在的线程:%@",[NSThread currentThread]);
    dispatch_sync(dispatch_get_main_queue(), ^{
        for (int i = 0; i<2; i++) {
            sleep(1);
            NSLog(@"主队列同步任务1 = %d",i);
            NSLog(@"当前线程:%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(dispatch_get_main_queue(), ^{
        for (int i = 0; i<2; i++) {
            sleep(1);
            NSLog(@"主队列同步任务2 = %d",i);
            NSLog(@"当前线程:%@",[NSThread currentThread]);
        }
    });
}

执行结果

主队列+异步任务

由于主队列是串行队列,所以主队列+异步任务就相当于串行队列+异步任务

代码

-(void)mainAsysncText {
    NSLog(@"当前所在的线程:%@",[NSThread currentThread]);
    NSLog(@"主队列异步任务开始");
    dispatch_async(dispatch_get_main_queue(), ^{
        for (int i = 0; i<2; i++) {
            sleep(1);
            NSLog(@"主队列异步任务1 = %d",i);
            NSLog(@"当前线程:%@",[NSThread currentThread]);
        }
    });
    dispatch_async(dispatch_get_main_queue(), ^{
        for (int i = 0; i<2; i++) {
            sleep(1);
            NSLog(@"主队列异步任务2 = %d",i);
            NSLog(@"当前线程:%@",[NSThread currentThread]);
        }
    });
    NSLog(@"主队列异步任务结束");
}

运行结果

效果等同于串行队列+异步任务