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,) // 默认优先级
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(基础篇)的更多相关文章
- iOS多线程开发之GCD(死锁篇)
上篇和中篇讲解了什么是GCD,如何使用GCD,这篇文章将讲解使用GCD中将遇到的死锁问题.有兴趣的朋友可以回顾<iOS多线程开发之GCD(上篇)>和<iOS多线程开发之GCD(中篇) ...
- iOS多线程开发之GCD(中级篇)
前文回顾: 上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue:第二 ...
- iOS多线程开发之GCD(中篇)
前文回顾: 上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue:第二 ...
- iOS多线程开发之NSOperation - 快上车,没时间解释了!
一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强 ...
- iOS多线程开发之NSOperation
一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强 ...
- iOS 开发之 GCD 基础
header{font-size:1em;padding-top:1.5em;padding-bottom:1.5em} .markdown-body{overflow:hidden} .markdo ...
- iOS开发之GCD基础
重新回顾.学习GCD.Block.先贴出一篇不错的讲解GCD基础使用的文章 原文地址:http://blog.csdn.net/aolan1108/article/details/17283415 做 ...
- iOS 多线程开发之OperationQueue(二)NSOperation VS GCD
原创Blog.转载请注明出处 blog.csdn.net/hello_hwc 欢迎关注我的iOS SDK具体解释专栏 http://blog.csdn.net/column/details/huang ...
- iOS多线程开发之NSThread
一.NSThread基本概念 NSThread是基于线程使用,轻量级的多线程编程方法(相对GCD和NSOperation),一个NSThread对象代表一个线程,需要手动管理线程的生命周期,处理线程同 ...
随机推荐
- Python 学习笔记:根据输入年月区间,返回期间所有的月份
目的: 给定一个年月区间,比如:2019.01 至 2019.05,要求返回一个包含期间所有的月份的列表,比如:['2019.01', '2019.02', '2019.03', '2019.04', ...
- Web API接口
Web API接口 一.什么是Web API接口 通过网络,规定了前后台信息交互规则的url链接,也就是前后台信息交互的媒介 Web API接口和一般的url链接还是有区别的,Web API接口简单概 ...
- 吴裕雄--天生自然TensorFlow高层封装:Keras-TensorFlow API
# 1. 模型定义. import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist_ ...
- debian8.8安装谷歌浏览器
第一步:下载: wget https://dl.google.com/linux/direct/google-chrome-stable_current_i386.deb //32位 wget h ...
- day55-mysql-用户权限、修改秘密、忘记密码
.用户权限:新创建的用户没有库,如果想让新用户访问我的库,必须给它授权才可以.我在使用的navicat要关闭新用户的连接才可以授权给它. .创建用户 '; -- 创建用户 .移除用户 drop use ...
- springcloud-netflix组件学习-未完待续!!!
SpringCloud学习 Spring Cloud是什么鬼? Spring Cloud是一系列框架的有序集合.它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现 ...
- [SDOI2019]染色(DP)
好神的题啊! 看了这题只会第一个subtask,又参考了HN-CJ鸽王zsy的题解,实在太菜了. 暴力转移是O(nc2),很显然没有分.考虑子任务1,2,只需要转移包含已染色格子的列,然后状态数只有O ...
- log4j日志配置和使用
一.日志配置变量参数说明 1. 日志设置说明:# log4j.rootLogger = debug,stdout,D,E# 等号之后的值表示appender对象,每个apperder对象表示一个日志输 ...
- iOS分段选择器、旅行App、标度尺、对对碰小游戏、自定义相册等源码
iOS精选源码 企业级开源项目,模仿艺龙旅行App 标签选择器--LeeTagView CSSegmentedControl常用的分段选择器,简单易用! 仿微信左滑删除 IOS左滑返回 输入框 iOS ...
- 吴裕雄--天生自然C语言开发:enum(枚举)
enum DAY { MON=, TUE, WED, THU, FRI, SAT, SUN }; enum DAY { MON=, TUE, WED, THU, FRI, SAT, SUN }; en ...