总纲:

  • 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,) // 默认优先级
dispatch_queue_t globalDispatchQueueDefault = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,) // 低优先级
dispatch_queue_t globalDispatchQueueLow = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,) // 后台优先级
dispatch_queue_t globalDispatchQueueBackgroud = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_GACKGROUND,)

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

- (void)testMainGlobalDispatchQueue{

    // 创建全局并发队列,默认优先级
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{ // 可并行处理的任务(耗时操作)代码放在这里 // 获取主线程,处理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");
}

打印结果:

-- ::10.963 Test[:] asyncConcurrentTask---start
-- ::10.963 Test[:] asyncConcurrentTask---end
-- ::10.963 Test[:] Task3------<NSThread: 0x60800007cdc0>{number = , name = (null)}
-- ::10.963 Test[:] Task2------<NSThread: 0x60800007d1c0>{number = , name = (null)}
-- ::10.963 Test[:] Task1------<NSThread: 0x600000074e80>{number = , 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---");
}

打印结果:

-- ::04.725 Test[:] syncConcurrentTask---start---
-- ::04.725 Test[:] Task1---<NSThread: 0x608000067540>{number = , name = main}
-- ::04.726 Test[:] Task2---<NSThread: 0x608000067540>{number = , name = main}
-- ::04.726 Test[:] Task3---<NSThread: 0x608000067540>{number = , name = main}
-- ::04.726 Test[:] 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---");
}

打印结果:

-- ::27.068 Test[:] asyncSerialTask---start---
-- ::27.068 Test[:] asyncSerialTask---end---
-- ::27.068 Test[:] Task1---<NSThread: 0x600000071e00>{number = , name = (null)}
-- ::27.069 Test[:] Task2---<NSThread: 0x600000071e00>{number = , name = (null)}
-- ::27.069 Test[:] Task3---<NSThread: 0x600000071e00>{number = , 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---");
}

打印结果:

-- ::48.948 Test[:] syncSerialTask---start---
-- ::48.948 Test[:] Task1---<NSThread: 0x600000076640>{number = , name = main}
-- ::48.949 Test[:] Task2---<NSThread: 0x600000076640>{number = , name = main}
-- ::48.949 Test[:] Task3---<NSThread: 0x600000076640>{number = , name = main}
-- ::48.949 Test[:] 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---");
}

打印结果:

-- ::36.828 Test[:] asyncMainTask---start---
-- ::36.828 Test[:] asyncMainTask---end---
-- ::36.834 Test[:] Task1---<NSThread: 0x608000072480>{number = , name = main}
-- ::36.834 Test[:] Task2---<NSThread: 0x608000072480>{number = , name = main}
-- ::36.834 Test[:] Task3---<NSThread: 0x608000072480>{number = , 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---");
}

打印结果:

-- ::31.860 Test[:] syncMainTask---start---

结论:

发生死锁,程序崩溃。

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

iOS多线程开发之离不开的GCD(上篇)的更多相关文章

  1. iOS多线程开发

    概览 大家都知道,在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算.可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行.但是机器码是按顺序执行的,一个复杂的多步操 ...

  2. iOS多线程开发资源抢夺和线程间的通讯问题

    说到多线程就不得不提多线程中的锁机制,多线程操作过程中往往多个线程是并发执行的,同一个资源可能被多个线程同时访问,造成资源抢夺,这个过程中如果没有锁机制往往会造成重大问题.举例来说,每年春节都是一票难 ...

  3. ios 多线程开发(一)简介

    简介 线程是在一个程序中并发的执行代码的方法之一.虽然有一些新的技术(operations, GCD)提供了更先进高效的并发实现,OS X和iOS同时也提供了创建和维护线程的接口. 这里将要介绍线程相 ...

  4. iOS多线程开发--NSThread NSOperation GCD

    多线程 当用户播放音频.下载资源.进行图像处理时往往希望做这些事情的时候其他操作不会被中 断或者希望这些操作过程中更加顺畅.在单线程中一个线程只能做一件事情,一件事情处理不完另一件事就不能开始,这样势 ...

  5. ios多线程开发总结

    1>无论使用哪种方法进行多线程开发,每个线程启动后并不一定立即执行相应的操作,具体什么时候由系统调度(CPU空闲时就会执行). 2>更新UI应该在主线程(UI线程)中进行,并且推荐使用同步 ...

  6. ios多线程开发的常用三种方式

    1.NSThread 2.NSOperationQueue 3.GCD NSThread: 创建方式主要有两种: [NSThread detachNewThreadSelector:@selector ...

  7. ios 多线程开发(二)线程管理

    线程管理 iOS和OS X中每一个进程(或程序)由一个或多个线程组成.程序由一个运行main方法的线程开始,中间可以产生其他线程来执行一些指定的功能. 当程序产生一个新线程后,这个线程在程序进程空间内 ...

  8. ios 多线程开发(三)Run Loops

    Run loops是线程相关的一些基本东西.一个run loop是一个处理消息的循环.用来处理计划任务或者收到的事件.run loop的作用是在有事做的时候保持线程繁忙,没事的时候让线程挂起. Run ...

  9. ios多线程开发基础

    多线程编程:下载数据时,开辟子线程,减少阻塞时间,和主线程并发运行,提升用户体验 1.Thread 1>新建Thread对象,带一selector方法,调用start方法,开启子线程 2> ...

随机推荐

  1. Linux 下按时间顺序批量删除文件

    ls -lrt| awk '{print $9}'| head -n 10 | xargs rm -rf 1.文件按时间排序: 2.获取文件名字: 3.取前10个文件 4.删除文件

  2. C#中设置TextBox控件中仅可以输入数字且设置上限

    首先设置只可以输入数字: 首先设置TextBox控件的KeyPress事件:当用户按下的键盘的键不在数字位的话,就禁止输入 private void textBox1_KeyPress(object ...

  3. 【JS中循环嵌套常见的六大经典例题+六大图形题,你知道哪几个?】

    首先,了解一下循环嵌套的特点:外层循环转一次,内层循环转一圈. 在上一篇随笔中详细介绍了JS中的分支结构和循环结构,我们来简单的回顾一下For循环结构: 1.for循环有三个表达式,分别为: ①定义循 ...

  4. c++堆与栈的简单认识

    堆: 操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删 除,并将该结点的空间分配给程序,另外,对于大多数 ...

  5. UICollectionView左对齐流水布局、右对齐流水布局

    在平时使用的app中会经常碰到一些规格选择,筛选,标签等页面,这些页面的布局展示通常是左对齐流水布局.实现类似这样的左对齐流水布局有多种方式,如果选项少的话可以直接用UIButton实现.现在我们有一 ...

  6. Java微服务框架

    Java的微服务框架dobbo.spring boot.redkale.spring cloud 消息中间件RabbitMQ.Kafka.RocketMQ

  7. Include promo/activity effect into the prediction (extended ARIMA model with R)

    I want to consider an approach of forecasting I really like and frequently use. It allows to include ...

  8. RedHat7上安装MySQL5.7.16

    1.查看系统中是否已将安装MySQL,如果安装了,需要卸载. [root@chenguo etc]# rpm -qa|grep -i mysql 2.创建用户和组 [root@chenguo ~]# ...

  9. Javaweb---服务器Tomcat与Eclipse的关联

    1.与eclipse的关联 打开eclipse-->window-->preferences 在搜索框输入->server->进行搜索,选择-–>server and R ...

  10. nodeJS之TCP模块net

    前面的话 TCP服务在网络应用中十分常见,目前大多数的应用都是基于TCP搭建而成的.net模块提供了一个异步网络包装器,用于TCP网络编程,它包含了创建服务器和客户端的方法.本文将详细介绍nodeJS ...