凌雪
2018-10-23
来源 :网络
阅读 1368
评论 0
摘要:本文将带你了解IOS开发入门iOS多线程之GCD概念及使用教程,希望本文对大家学IOS有所帮助。
本文将带你了解IOS开发入门iOS多线程之GCD概念及使用教程,希望本文对大家学IOS有所帮助。
iOS多线程之GCD概念及使用教程。
GCD的使用姿势全解
经过前一篇文章的学习,可以发现直接使用NSThread来编写多线程陈谷有不少问题,线程在执行完成后就会退出,每次执行任务都需要创建一个线程很浪费资源,其次是需要我们自行进行同步操作,自行管理线程的生命周期,如果要编写并发的代码或者多核的真正并行处理的代码就比较复杂了。
本篇文章将会介绍一个抽象层次更高的多线程编写方式GCD,GCD全称Grand Central Dispatch是苹果提供的一个多核编程的解决方案,在真正意义上实现了并行操作,而不是并发。
GCD使用线程池模型来执行用户提交的任务,所以它比较节约资源,不需要为每个任务都重新创建一个新的线程,GCD不需要自行编写并行代码,而是自动进行多核的并行计算,自动管理线程的生命周期,如:使用线程池管理线程的创建和销毁,线程的调度,任务的调度等,用户只需要编写任务代码并提交即可。
GCD中有两个比较重要的概念:任务和队列。
GCD的任务
任务顾名思义就是我们需要执行的代码块,可以是一个方法也可以是一个block,就是我们需要线程为我们完成的工作,编写完成的任务只需提交给GCD的队列,即可自动帮我们完成任务的调度,以及线程的调度,可以很方便的以多线程的方式执行。
GCD的队列
队列用于管理用户提交的任务,GCD的队列有两种形式,串行队列和并发队列:
串行队列: GCD底层只维护一个线程,任务只能串行依次执行。 并发队列: GCD底层使用线程池维护多个线程,任务可并发执行。
不论是串行队列还是并发队列都使用FIFO 先进先出的方式来管理用户提交的任务。
对于串行队列来说,GCD每次从串行队列的队首取一个任务交给唯一的一个线程来处理,直到前一个任务完成后,才继续从队列中取下一个任务来执行,因此,串行队列中的任务执行严格按照提交顺序,并且后一个任务必须等前一个任务执行完成后才可以执行。
对于并发队列来说,GCD每次从并发队列的队首取一个任务,并将这个任务按照任务调度分发给多个线程中的某一个线程,此时不需要等待其完成,如果队列中还有其他任务继续从队列中取出并分发给某一个线程来执行,由于底层由线程池管理多个线程,每个任务的时间复杂度不同再加上线程调度的影响,后提交的任务可能先执行完成。但对于单个线程来说,只能按顺序执行,比如某个线程被安排了多个任务,那这个线程就只能按提交顺序依次执行任务。
所以,我们在使用GCD时也就很简单了,只需要创建或获取系统队列、编写任务并提交任务到队列即可。首先举一个下载图片的栗子,这个栗子和第一篇讲解NSThread的栗子一样,但是使用GCD来实现:
<code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code>//获取一个优先级默认的全局并发队列,并以异步的方式提交任务执行
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//下载图片
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1509003055&di=ef9641b620fc103323df445bf796cb13&imgtype=jpg&er=1&src=http%3A%2F%2Fwscont2.apps.microsoft.com%2Fwinstore%2F1x%2Fea9a3c59-bb26-4086-b823-4a4869ffd9f2%2FScreenshot.398115.100000.jpg"]]];
//获取主队列,在主线程中更新UI,并以异步方式提交任务
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});</code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code>
上面的栗子非常简单,不需要我们手动创建线程即可实现多线程的并发编程,如果现在还看不懂没关系,学完本章内容你一定会懂。
首先看一下GCD为我们提供了哪些创建队列或获取系统队列的方法:
<code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code>//获取当前执行该方法的队列,被废弃了,最好不要使用
dispatch_queue_t dispatch_get_current_queue(void);
/*
获取主队列,即与主线程相关联的队列
如果需要提交任务到主线程使用该方法获取主线程的主队列即可
主队列是串行队列因为只维护主线程一个线程
*/
dispatch_queue_t dispatch_get_main_queue(void);
/*
获取一个全局的并发队列
identifier指定该队列的优先级可选值有:
DISPATCH_QUEUE_PRIORITY_HIGH 2
DISPATCH_QUEUE_PRIORITY_DEFAULT 0
DISPATCH_QUEUE_PRIORITY_LOW (-2)
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
flags未用到传个0得了
*/
dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags);
/*
创建一个队列
label 队列的名称
attr 队列的属性可选值有:
DISPATCH_QUEUE_SERIAL 创建一个串行队列
DISPATCH_QUEUE_CONCURRENT 创建一个并发队列
通过这种方式可以自己维护一个队列
*/
dispatch_queue_t dispatch_queue_create(const char *_Nullable label, dispatch_queue_attr_t _Nullable attr);</code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code>
具体获取相关队列的方法如下:
<code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code>//获取串行主队列
dispatch_queue_t mainQueue = dispatch_get_mian_queue();
//获取一个默认优先级的并发队列
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT);
//自定义创建一个名称为myConcurrentQueue的并发队列
dispatch_queue_t myConcurrentQueue = dispatch_queue_create("myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);</code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code>
队列创建完成以后就可以编写任务并提交了,接下来将介绍两种提交执行了,接下来介绍两种执行方式,同步执行和异步执行。
同步执行: 阻塞当前线程,直到任务执行完成当前线程才可继续执行 异步执行: 不阻塞当前线程,可能使用其他线程来执行任务,不需要等待任务完成当前线程即可立即继续执行
关于同步/异步,阻塞/非阻塞建议看UNIX网络编程 卷一有详细的解释,此处不再赘述了。
看一下GCD提交执行任务的具体方法:
<code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code><code>/*
以异步方式执行任务,不阻塞当前线程
queue 管理任务的队列,任务最终交由该队列来执行
block block形式的任务,该block返回值、形参都为void
*/
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
/*
同上
context 是一个void*的指针,作为work的第一个形参
work 是一个函数指针,指向返回值为void 形参为void*的函数,且形参不能为NULL,也就是说context一定要传
使用起来不方便,一般不怎么用,需要使用C函数,也可以使用OC方法通过传递IMP来执行但是会有编译警告
*/
void dispatch_async_f(dispatch_queue_t queue, void *_Nullable context, dispatch_function_t work);
/*
以同步方式执行任务,阻塞当前线程,必须等待任务完成当前线程才可继续执行
*/
void dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
//同上
void dispatch_sync_f(dispatch_queue_t queue, void *_Nullable context, dispatch_function_t work);
/*
以同步方式提交任务,并重复执行iterations次
iterations 迭代执行次数
queue 管理任务的队列,任务最终交由该队列来执行
block block形式的任务,该block返回值为void形参为iterations迭代次数
*/
void dispatch_apply(size_t iterations, dispatch_queue_t queue, DISPATCH_NOESCAPE void (^block)(size_t));
//同上
void dispatch_apply_f(size_t iterations, dispatch_queue_t queue, void *_Nullable context, void (*work)(void *_Nullable, size_t));
/*
以异步方式提交任务,在when时间点提交任务
queue 管理任务的队列,任务最终交由该队列来执行
block block形式的任务,该block返回值、形参都为void
*/
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
//同上
void dispatch_after_f(dispatch_time_t when, dispatch_queue_t queue, void *_Nullable context, dispatch_function_t work);
/*
以异步方式提交任务,会阻塞queue队列,但不阻塞当前线程
queue 管理任务的队列,任务最终交由该队列来执行
需要说明的是,即时使用并发队列,该队列也会被阻塞,前一个任务执行完成才能执行下一个任务
block block形式的任务,该block返回值、形参都为void
*/
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
//同上
void dispatch_barrier_async_f(dispatch_queue_t queue, void *_Nullable context, dispatch_function_t work);
/*
以同步方式提交任务,会阻塞queue队列,也会阻塞当前线程
queue 管理任务的队列,任务最终交由该队列来执行
同样的,即时是并发队列该队列也会被阻塞,需要等待前一个任务完成,同时线程也会阻塞
block block形式的任务,该block返回值、形参都为void
*/
void dispatch_barrier_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
//同上
void dispatch_barrier_sync_f(dispatch_queue_t queue, void *_Nullable context, dispatch_function_t work);
/*
底层线程池控制block任务在整个应用的生命周期内只执行一次
predicate 实际为long类型,用于判断是否执行过
block block形式的任务,该block返回值、形参都为void
该方法常用于实现单例类,以及结合RunLoop创建一个常驻内存的线程
*/
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
</code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code></code>
猛的一看常用的方法就有十二种呢,但是可以发现每一类方法都提供了block任务和function任务两种形式,所以常用的也就七种,只是对应了block版本和函数版本。接下来先介绍最常用的同步执行和异步执行,其他的方法后文会讲。
单拎出来同步/异步很好理解,但是结合了串行队列和并发队列以后情况就有点复杂了,同步/异步执行和串行/并发队列两两组合就有四种组合方式,接下来我们一一查看相关栗子:
<div class="line numbe
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之IOS频道!
喜欢 | 0
不喜欢 | 0
您输入的评论内容中包含违禁敏感词
我知道了

请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号