美文网首页
iOS GCD基本介绍和使用

iOS GCD基本介绍和使用

作者: 没脑子的程序员 | 来源:发表于2019-03-01 16:05 被阅读0次

1.GCD简介

Overview

Grand Central Dispatch (GCD) comprises language features, runtime libraries, and system enhancements that provide systemic, comprehensive improvements to the support for concurrent code execution on multicore hardware in macOS, iOS, watchOS, and tvOS.

The BSD subsystem, Core Foundation, and Cocoa APIs have all been extended to use these enhancements to help both the system and your application to run faster, more efficiently, and with improved responsiveness. Consider how difficult it is for a single application to use multiple cores effectively, let alone doing it on different computers with different numbers of computing cores or in an environment with multiple applications competing for those cores. GCD, operating at the system level, can better accommodate the needs of all running applications, matching them to the available system resources in a balanced fashion.

GCD包含了语言特性,运行时库和系统改善,这些功能提供了影响全局、全面的改善以支持并发代码在多核硬件中的处理(包括macOS、iOS、watchOS、和tvOS)。

BSD子系统、核心基础、和Cocoa APIs全都扩展使用了这些改善以帮助系统和你的应用运行得更快,更有效率,并且提高了响应能力。考虑到使一个单独的应用可以有效率地使用多核有多困难,更不用说在拥有不同数量核心的各种电脑上或者在多个应用竞争使用这些核心的环境中。GCD,在系统层面运转,可以更好满足运行应用的需求,并以平衡的方式让他们匹配可用的系统资源。

2.GCD的几个概念

使用GCD我们并不需要管理线程的使用,而只需要关注任务的执行方式。

GCD的使用需要了解几个概念:队列、线程、任务

2.1队列

GCD中有两种队列,一种是串行队列,一种是并行队列,队列都遵循FIFO(先进先出)的原则来给线程分配任务。

  • 串行队列

    • 串行队列里的任务,按顺序被分配到一个线程执行,同时只会有一个任务在执行。
  • 并行队列

    • 并行队列里的任务,按顺序被分配到多个线程,同时执行,但执行完成的顺序不一定按照添加任务的顺序。

2.2线程

线程是实际执行任务的地方,每条线程同时只能执行一个任务,队列的职责就是分配任务给线程

2.3任务

任务就是我们要执行的代码,在GCD中通常放在dispatch_block_t对象中,有两种执行方式:sync(同步)和async(异步)

  • 同步执行(sync)

    *同步执行添加任务到指定队列,使当前队列进入等待状态,直到任务执行完毕,才会继续执行后续的任务
    *同步执行不具备开启新线程的能力,会在当前线程中执行任务
  • 异步执行(async)

    *异步执行添加任务到指定队列时,当前队列不会进入等待,继续执行后面的任务
    *异步执行可以开启新的线程,在新的线程中执行任务

3.GCD的基本使用

3.1 获取队列/创建队列

  • 获取主队列

通过dispatch_get_main_queue()方法可以获取主队列,主队列是一个特殊的串行队列,主队列里所有的任务都会在主线程执行

  • 获取全局队列

通过dispatch_get_global_queue可以获取全局队列,全局队列是系统创建的并行队列,该方法需要传入两个参数,第一个是优先级,优先级高的队列会比优先级低的队列分配到更多的资源,也更快开始执行。第二个参数暂时没有用,默认传0。

优先级一共四种,如下:

#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_create,方法的第一个参数是传入唯一标识符,第二个参数决定队列的类型是串行队列还是并行队列

//创建并行队列
dispatch_queue_t queue = dispatch_queue_create("com.test.test", DISPATCH_QUEUE_SERIAL);
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.test.test", DISPATCH_QUEUE_CONCURRENT);

3.2创建任务

上面见过,任务分为同步执行和异步执行,GCD提供了同步执行方法dispatch_sync和异步执行方法dispatch_async,都需要传入目标队列和任务代码的block

//创建同步执行任务
dispatch_sync(queue, ^{
    //任务代码
});
//创建异步执行任务
dispatch_async(queue, ^{
    // 任务代码
});

3.3任务执行

任务的执行与队列的类型和任务的执行方式都有关系,我们有两种队列和两种执行方式,那么我们就会四种组合,我们需要了解只四种组合下任务的执行情况,不过我们还有一个特殊的主队列(全局队列和并行队列没有区别),所以一共有6种情况:

串行队列、同步执行
串行队列、异步执行
并行队列、同步执行
并行队列、异步执行
主队列、同步执行
主队列、异步执行

串行队列 并行队列 主队列
同步执行 不开启新的线程,任务在当前线程上,按添加的顺序一个一个执行 不开启新的线程,任务在当前线程上,按添加的顺序一个一个执行 如何当前线程是主线程,那么线程发生死锁,整个应用卡卡死,如果当前线程不是主线程,不开启新的线程,任务在当前线程上,按添加的顺序一个一个执行
异步执行 开启一条新的线程,在新开启的线程上执行,任务按添加的顺序一个一个执行 开启一条或多条线程,任务按顺序分配到各条线程上执行 不开启新的线程,任务在当前线程(主线程)上,按添加的顺序一个一个执行

3.3.1串行队列、同步执行

不开启新的线程,任务在当前线程上,按添加的顺序一个一个执行

    //创建串行队列
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    
    NSLog(@"serialQueue sync begin %@",[NSThread currentThread]);
    
    //同步执行任务1
    dispatch_sync(serialQueue, ^{
        NSLog(@"任务1开始 %@",[NSThread currentThread]);
        sleep(2);  //耗时操作,证明任务时按顺序执行
        NSLog(@"任务1完成 %@",[NSThread currentThread]);
    });
    //同步执行任务2
    dispatch_sync(serialQueue, ^{
        NSLog(@"任务2 %@",[NSThread currentThread]);
    });
    //同步执行任务3
    dispatch_sync(serialQueue, ^{
        NSLog(@"任务3 %@",[NSThread currentThread]);
    });
    
    NSLog(@"serialQueue sync end %@",[NSThread currentThread]);

打印结果

18:11:53.101 serialQueue sync begin <NSThread: 0x7ff41fe04990>{number = 1, name = main}
18:11:53.102 任务1开始 <NSThread: 0x7ff41fe04990>{number = 1, name = main}
18:11:55.103 任务1完成 <NSThread: 0x7ff41fe04990>{number = 1, name = main}
18:11:55.104 任务2 <NSThread: 0x7ff41fe04990>{number = 1, name = main}
18:11:55.104 任务3 <NSThread: 0x7ff41fe04990>{number = 1, name = main}
18:11:55.104 serialQueue sync end <NSThread: 0x7ff41fe04990>{number = 1, name = main}

从打印结果可以看出来

  • 所有的任务都是在当前线程(主线程)上执行的,这和同步执行没有开启新线程的能力是符合的
  • 所有任务都是按顺序完成的,中间我们模拟了耗时的操作占用了线程,后续的任务都没有开始执行。
  • serialQueue sync beginserialQueue sync end也是按照顺序执行,因为同步执行会将当前线程挂起(这里是主队列),所以所有的任务都是按顺序执行

3.3.2串行队列、异步执行

开启一条新的线程,在新开启的线程上执行,任务按添加的顺序一个一个执行

    //创建串行队列
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    
    NSLog(@"serialQueue sync begin %@",[NSThread currentThread]);
    
    //同步执行任务1
    dispatch_async(serialQueue, ^{
        NSLog(@"任务1%@",[NSThread currentThread]);
    });
    //同步执行任务2
    dispatch_async(serialQueue, ^{
        NSLog(@"任务2开始 %@",[NSThread currentThread]);
        sleep(2);  //耗时操作,证明任务时按顺序执行
        NSLog(@"任务2完成 %@",[NSThread currentThread]);
    });
    //同步执行任务3
    dispatch_async(serialQueue, ^{
        NSLog(@"任务3 %@",[NSThread currentThread]);
    });
    
    NSLog(@"serialQueue sync end %@",[NSThread currentThread]);

运行了多次,取其中两次打印结果

//第一次
23:22:01.453 serialQueue sync begin <NSThread: 0x6000033d1580>{number = 1, name = main}
23:22:01.453 serialQueue sync end <NSThread: 0x6000033d1580>{number = 1, name = main}
23:22:01.453 任务1 <NSThread: 0x6000033a8000>{number = 3, name = (null)}
23:22:01.454 任务2开始 <NSThread: 0x6000033a8000>{number = 3, name = (null)}
23:22:03.456 任务2完成 <NSThread: 0x6000033a8000>{number = 3, name = (null)}
23:22:03.456 任务3 <NSThread: 0x6000033a8000>{number = 3, name = (null)}

第二次
11:48:08.693 serialQueue sync begin <NSThread: 0x7fd4fc6093c0>{number = 1, name = main}
11:48:08.695 任务1 <NSThread: 0x7fd4fc552150>{number = 3, name = (null)}
11:48:08.695 serialQueue sync end <NSThread: 0x7fd4fc6093c0>{number = 1, name = main}
11:48:08.696 任务2开始 <NSThread: 0x7fd4fc552150>{number = 3, name = (null)}
11:48:10.698 任务2完成 <NSThread: 0x7fd4fc552150>{number = 3, name = (null)}
11:48:10.698 任务3 <NSThread: 0x7fd4fc552150>{number = 3, name = (null)}

从打印结果来看

  • 两次的结果不一致,异步任务不会让当前线程进入等待状态,所有当异步执行任务1时,serialQueue sync end也会正常被执行,两者的处理完成先后顺序不一定。
  • 队列是串行队列,所有添加到队伍里面的任务还是按顺序一个一个执行。
  • 3个任务的执行线程都是线程3,证明了异步执行是有开启新的线程的能力的,但是由于是串行队列,所有只能开启一个

3.3.3并行队列、同步执行

不开启新的线程,在当前线程中按顺序一个一个执行任务

    //创建串行队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_CONCURRENT);
    
    NSLog(@"concurrentQueue sync begin %@",[NSThread currentThread]);
    
    //同步执行任务1
    dispatch_sync(concurrentQueue, ^{
        NSLog(@"任务1开始 %@",[NSThread currentThread]);
        sleep(2);  //耗时操作,证明任务时按顺序执行
        NSLog(@"任务1完成 %@",[NSThread currentThread]);
    });
    //同步执行任务2
    dispatch_sync(concurrentQueue, ^{
        NSLog(@"任务2 %@",[NSThread currentThread]);
    });
    //同步执行任务3
    dispatch_sync(concurrentQueue, ^{
        NSLog(@"任务3 %@",[NSThread currentThread]);
    });
    
    NSLog(@"concurrentQueue sync end %@",[NSThread currentThread]);

打印结果

14:29:29.557 concurrentQueue sync begin <NSThread: 0x7fae76c29ec0>{number = 1, name = main}
14:29:29.558 任务1开始 <NSThread: 0x7fae76c29ec0>{number = 1, name = main}
14:29:31.560 任务1完成 <NSThread: 0x7fae76c29ec0>{number = 1, name = main}
14:29:31.560 任务2 <NSThread: 0x7fae76c29ec0>{number = 1, name = main}
14:29:31.560 任务3 <NSThread: 0x7fae76c29ec0>{number = 1, name = main}
14:29:31.560 concurrentQueue sync end <NSThread: 0x7fae76c29ec0>{number = 1, name = main}
  • 任务按顺序进行执行,因为同步执行不具备开启新线程的能力,即便使用的是在并行线程上也是,线程同时只能处理一个任务,所以所有任务只能按照加入队列的顺序去执行。

3.3.4并行队列、异步执行

开启多个线程,同时处理多个任务

    //创建串行队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_CONCURRENT);
    
    NSLog(@"concurrentQueue sync begin %@",[NSThread currentThread]);
    
    //同步执行任务1
    dispatch_async(concurrentQueue, ^{
        NSLog(@"任务1开始 %@",[NSThread currentThread]);
        sleep(2);  //耗时操作,证明任务时按顺序执行
        NSLog(@"任务1完成 %@",[NSThread currentThread]);
    });
    //同步执行任务2
    dispatch_async(concurrentQueue, ^{
        NSLog(@"任务2 %@",[NSThread currentThread]);
    });
    //同步执行任务3
    dispatch_async(concurrentQueue, ^{
        NSLog(@"任务3 %@",[NSThread currentThread]);
    });
    
    NSLog(@"concurrentQueue sync end %@",[NSThread currentThread]);

打印结果

14:43:53.069 concurrentQueue sync begin <NSThread: 0x7ff068e03a60>{number = 1, name = main}
14:43:53.070 concurrentQueue sync end <NSThread: 0x7ff068e03a60>{number = 1, name = main}
14:43:53.070 任务1开始 <NSThread: 0x7ff068c4f820>{number = 3, name = (null)}
14:43:53.070 任务3 <NSThread: 0x7ff068c37050>{number = 4, name = (null)}
14:43:53.070 任务2 <NSThread: 0x7ff068c32920>{number = 5, name = (null)}
14:43:55.074 任务1完成 <NSThread: 0x7ff068c4f820>{number = 3, name = (null)}
  • 开启了多个线程,3个任务在不同的线程里同时执行,任务1在进行耗时操作时,任务2与任务3继续进行。
  • 异步任务不会让当前线程进入等待状态,所以concurrentQueue sync end也会同时开始执行

3.3.5主队列、同步执行

会造成死锁,应用卡死没有任何反应

dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"mainQueue sync");
});
  • 同步执行会让当前队列(此时是主队列)进入等待状态,等待任务处理完成后继续执行,然后将任务加入到目标队列(此时是主队列),而当前队列处于等待状态,不会去执行后面的任务,进入了互相等待的状态,因此造成了死锁,而所有UI的刷新都是通过主队列来执行了,所以整个应用都卡死了。

3.3.6主队列、异步执行

任务按顺序执行,所有任务在主线程上执行

    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"任务1 %@",[NSThread currentThread]);
    });
    
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"任务2 %@",[NSThread currentThread]);
    });
    
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"任务3 %@",[NSThread currentThread]);
    });
15:05:20.811 任务1 <NSThread: 0x7ff185d0bf40>{number = 1, name = main}
15:05:20.811 任务2 <NSThread: 0x7ff185d0bf40>{number = 1, name = main}
15:05:20.811 任务3 <NSThread: 0x7ff185d0bf40>{number = 1, name = main}
  • 所有任务在主线程上执行,与异步执行会开启新线程的说法产生了冲突,这里主要是主队列是一个特殊的队列,即使是使用异步执行也不会开启新的线程
  • 因为主队列是特殊的串行对了,所以任务时按顺序执行,与异步执行、串行队列的特点一样。

相关文章

网友评论

      本文标题:iOS GCD基本介绍和使用

      本文链接:https://www.haomeiwen.com/subject/bmskxqtx.html