iOS底层探索之多线程十八,iOS多线程开发之GCD(基础篇)

 总纲:GCD基本概念GCD如何实现GCD如何使用队列和任务组合 一、GCD基本概念

     GCD 全称Grand Central Dispatch(大中枢队列调度),是一套低层API,提供了⼀种新的方法来进⾏并发程序编写。从基本功能上讲,GCD有点像NSOperationQueue,他们都允许程序将任务切分为多个单一任务,然后提交⾄⼯作队列来并发的或者串⾏的执行。GCD是C实现,⽐NSOpertionQueue更底层更高效,并且它不是Cocoa框架的一部分 并发任务会像NSOperationQueue那样基于系统负载来合适地并发进⾏,而串⾏行队列同一时间只执行单一任务,GCD的API很大程度上基于block。

    GCD并发编程的主要好处归纳

GCD可用于多核的并行运算GCD会自动利用更多的CPU内核(比如双核、四核)GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

二、GCD如何实现

     GCD主要由队列和任务两部分来实现,苹果官方对GCD是这样说明的:开发者要做的只是定义想执行的任务并追加到适当的Dispatch Queue中。Dispatch Queue是执行处理的等待队列,我们可以通过dispatch_async等API,在block语法中记述想要执行的处理并将其追加到Dispatch Queue中,Dispatch Queue是按照追加的顺序进行处理(先进先出FIFO)。

     多线程执行过程就是把任务放在队列中去执行的过程。那么在这里我们首先回顾一下基本概念:

   (一)进程/线程、任务/队列

     

 

       

 

 

 

 

 

 

 

   (二)同步/异步、并发/并行

    并发不一定等于并行

 (三)异步/同步任务 & 并行/串行队列的特点

  

     综上所述,iOS多线程编程使用GCD的最优原则是能不在阻碍主线程(又叫作UI线程)的情况下,开启新的线程(子线程)去处理耗时的操作,以便有效提高程序的执行效率和资源利用率,但是同时开启多个子线程也会引发许多其他的问题,如资源竞争、死锁、内存损耗等,所以要注意,这篇文章只是介绍GCD的使用,因此可能产生的问题我将会在这个系列后续篇章做介绍。

     GCD并发编程产生的作用归纳(考虑线程安全,不死锁的情况下效果):

能开启新的线程(子线程)

多个任务可以同时进行

不会阻塞主线程(又叫作UI线程)影响UI事件

 

三、GCD如何使用

    开发者要做的只是定义想执行的任务并追加到适当的队列(Dispatch Queue)中   

    1、创建队列(Dispatch Queue)

    第一种:通过GCD的API的dispatch_queue_create函数生成Dispatch Queue

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

 

    另外需要注意的点是:虽然有ARC编译器自动管理内存这一优秀技术,但生成的Dispatch Queue必须由程序员主动释放。

// 释放dispatch_release(exampleSerialDispatchQueue) // 持有dispatch_retain(exampleSerialDispatchQueue)

 

    第二种:直接使用系统提供的标准Dispatch Queue :Main Dispatch Queue和Global Dispatch Queue

  (1)Main Dispatch Queue:主线程中执行的Dispatch Queue,也就是Serial Dispatch Queue(串行队列),可以通过dispatch_get_main_queue()来获取。       

dispatch_queue_t mainDispatchQueue = dispath_get_main_queue();

    (2)  Global Dispatch Queue: 全局并发队列(Concurrent Dispatch Queue),GCD默认提供了全局的并发队列,可以通过dispatch_get_global_queue()获取。

// 高优先级dispatch_queue_t globalDispatchQueueHigh = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0)// 默认优先级dispatch_queue_t globalDispatchQueueDefault = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0) // 低优先级dispatch_queue_t globalDispatchQueueLow = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0)// 后台优先级dispatch_queue_t globalDispatchQueueBackgroud = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_GACKGROUND,0)

     一般来说,主线程(又叫做UI线程)主要处理UI事件,耗时操作(如I/O,数据库访问,网络资源加载等)则放在子线程中,等子线程操作完成后再回到主线程进行UI刷新,以下例举使用Main Dispatch Queue和Global Dispatch Queue的源码:

 

- (void)testMainGlobalDispatchQueue{// 创建全局并发队列,默认优先级dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 可并行处理的任务(耗时操作)代码放在这里// 获取主线程,处理UI事件dispatch_async(dispatch_get_main_queue(), ^{ // UI事件});});}

 

    2、创建任务

// 同步执行任务创建方法dispatch_sync(queue, ^{NSLog(@"%@",[NSThread currentThread]);// 这里放任务代码});// 异步执行任务创建方法dispatch_async(queue, ^{NSLog(@"%@",[NSThread currentThread]);// 这里放任务代码});

 

四、队列任务组合 

根据(二)中描述,GCD由队列和任务两部分组成,队列分为串行队列、并行队列、主队列,任务可分为同步和异步任务,这样可将队列与任务组合如下:

 

1、并行队列 & 异步执行

- (void) asyncConcurrentTask{NSLog(@"asyncConcurrentTask---start");dispatch_queue_t queue= dispatch_queue_create("com.beckwang.queue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{NSLog(@"Task1------%@",[NSThread currentThread]);});dispatch_async(queue, ^{NSLog(@"Task2------%@",[NSThread currentThread]);});dispatch_async(queue, ^{NSLog(@"Task3------%@",[NSThread currentThread]);});NSLog(@"asyncConcurrentTask---end");}

 打印结果:

2017-07-02 11:13:10.963 Test[6266:2853210] asyncConcurrentTask---start2017-07-02 11:13:10.963 Test[6266:2853210] asyncConcurrentTask---end2017-07-02 11:13:10.963 Test[6266:2854044] Task3------{number = 5, name = (null)}2017-07-02 11:13:10.963 Test[6266:2854059] Task2------{number = 4, name = (null)}2017-07-02 11:13:10.963 Test[6266:2854041] Task1------{number = 3, name = (null)}

  结论:

   (1) 开启了新线程

   (2) 任务之间不需要排队,且具有同时被执行的权利

 

2、并行队列 & 同步执行

- (void)syncConcurrentTask{dispatch_queue_t queue = dispatch_queue_create("com.beck.wang.queue", DISPATCH_QUEUE_CONCURRENT); NSLog(@"syncConcurrentTask---start---"); dispatch_sync(queue, ^{NSLog(@"Task1---%@", [NSThread currentThread]);});dispatch_sync(queue, ^{NSLog(@"Task2---%@", [NSThread currentThread]);});dispatch_sync(queue, ^{NSLog(@"Task3---%@", [NSThread currentThread]);});NSLog(@"syncConcurrentTask---end---");}

 打印结果:

2017-07-02 11:25:04.725 Test[6385:2940867] syncConcurrentTask---start---2017-07-02 11:25:04.725 Test[6385:2940867] Task1---{number = 1, name = main}2017-07-02 11:25:04.726 Test[6385:2940867] Task2---{number = 1, name = main}2017-07-02 11:25:04.726 Test[6385:2940867] Task3---{number = 1, name = main}2017-07-02 11:25:04.726 Test[6385:2940867] syncConcurrentTask---end---

  结论:

   (1) 不开启了新线程

   (2) 任务之间需要排队,按照追加顺序执行

 

3、串行队列 & 异步执行

- (void)asyncSerialTask{dispatch_queue_t queue = dispatch_queue_create("com.beckwang.queue", DISPATCH_QUEUE_SERIAL); NSLog(@"asyncSerialTask---start---");dispatch_async(queue, ^{NSLog(@"Task1---%@", [NSThread currentThread]);});dispatch_async(queue, ^{NSLog(@"Task2---%@", [NSThread currentThread]);});dispatch_async(queue, ^{NSLog(@"Task3---%@", [NSThread currentThread]);});NSLog(@"asyncSerialTask---end---");}

 打印结果:

2017-07-02 11:36:27.068 Test[6557:3008079] asyncSerialTask---start---2017-07-02 11:36:27.068 Test[6557:3008079] asyncSerialTask---end---2017-07-02 11:36:27.068 Test[6557:3008342] Task1---{number = 3, name = (null)}2017-07-02 11:36:27.069 Test[6557:3008342] Task2---{number = 3, name = (null)}2017-07-02 11:36:27.069 Test[6557:3008342] Task3---{number = 3, name = (null)}

 结论:

   (1) 开启了新线程

   (2) 任务之间需要排队,按照追加顺序执行

 

4、串行队列 & 同步执行

- (void)syncSerialTask{dispatch_queue_t queue = dispatch_queue_create("com.beckwang.queue", DISPATCH_QUEUE_SERIAL); NSLog(@"syncSerialTask---start---");dispatch_sync(queue, ^{NSLog(@"Task1---%@", [NSThread currentThread]);});dispatch_sync(queue, ^{NSLog(@"Task2---%@", [NSThread currentThread]);});dispatch_sync(queue, ^{NSLog(@"Task3---%@", [NSThread currentThread]);});NSLog(@"syncSerialTask---end---");}

 打印结果:

2017-07-02 13:17:48.948 Test[7238:3192943] syncSerialTask---start---2017-07-02 13:17:48.948 Test[7238:3192943] Task1---{number = 1, name = main}2017-07-02 13:17:48.949 Test[7238:3192943] Task2---{number = 1, name = main}2017-07-02 13:17:48.949 Test[7238:3192943] Task3---{number = 1, name = main}2017-07-02 13:17:48.949 Test[7238:3192943] syncSerialTask---end---

结论:

   (1) 不开启了新线程

   (2) 任务之间需要排队,按照追加顺序执行

 

5、主队列 & 异步执行

- (void)asyncMainTask{dispatch_queue_t queue = dispatch_get_main_queue(); NSLog(@"asyncMainTask---start---");dispatch_async(queue, ^{NSLog(@"Task1---%@", [NSThread currentThread]);});dispatch_async(queue, ^{NSLog(@"Task2---%@", [NSThread currentThread]);});dispatch_async(queue, ^{NSLog(@"Task3---%@", [NSThread currentThread]);});NSLog(@"asyncMainTask---end---");}

打印结果:

2017-07-02 13:19:36.828 Test[7272:3206224] asyncMainTask---start---2017-07-02 13:19:36.828 Test[7272:3206224] asyncMainTask---end---2017-07-02 13:19:36.834 Test[7272:3206224] Task1---{number = 1, name = main}2017-07-02 13:19:36.834 Test[7272:3206224] Task2---{number = 1, name = main}2017-07-02 13:19:36.834 Test[7272:3206224] Task3---{number = 1, name = main}

 结论:

   (1) 不开启了新线程

   (2) 任务之间需要排队,按照追加顺序执行

 

6、主队列 & 同步执行

- (void)syncMainTask{dispatch_queue_t queue = dispatch_get_main_queue(); NSLog(@"syncMainTask---start---");dispatch_sync(queue, ^{NSLog(@"Task1---%@", [NSThread currentThread]);});dispatch_sync(queue, ^{NSLog(@"Task2---%@", [NSThread currentThread]);});dispatch_sync(queue, ^{NSLog(@"Task3---%@", [NSThread currentThread]);});NSLog(@"syncMainTask---end---");}

 打印结果:

2017-07-02 13:22:31.860 Test[7335:3230988] syncMainTask---start---

结论:

发生死锁,程序崩溃。

 

好了GCD系列的上篇就写到这里,我将在后续系列中详细介绍GCD的队列系列和用法,以及使用GCD可能造成的问题及解决方案,水平有限,有不对的地方还望批评指正!

 

067丑面的秘密067太子驾到05566金牌棋牌下载04版工商管理专业知识与实务初级勘误表07AJAX风云062冒险岛0419收工kdl06055中烟香港CTIHK06第六章第6讲印花税06第六章赐福之地轶闻契约流程攻略01第一个产品064公车奇遇上1208103e2是实型常量吗00后的句子毁三观04欧洲杯捷克黄金一代现状07年注册会计师审计考点详解概论07除霜除雾分析方法pdf03科赫法则039潜艇更换电池画面首次曝光071喝牛奶黄恺三国杀设计者2021年的暑假作文800字