摘要:本文将带你了解IOS开发入门iOS多线程开发笔记,希望本文对大家学IOS有所帮助。
本文将带你了解IOS开发入门iOS多线程开发笔记,希望本文对大家学IOS有所帮助。
基本概念、术语:
进程(Precess):进程是操作系统管理和分配资源的最小单位,每个进程都有自己的内存空间、系统资源,至少有一个主线程和多个辅助线程。在iOS中,每个App运行的时候,都有对应的进程。
线程(Thread):线程则是操作系统具体的执行单元,代码的执行是在线程来完成的。在iOS中,线程的底层实现是基于POSIX thread
API的,也就是我们常说的pthread。 任务(Task):任务是我们抽象出来的需要执行的工作,一般指代一段代码。 同步 vs 异步
同步是指函数的调用会阻塞当前的线程,必须等待函数返回才能继续执行接下来的代码。
异步函数的调用则不会阻塞当前线程,函数调用之后立刻返回,一般通过回调函数来处理函数的执行结果。
异步函数能够有效的完成一些耗时的任务,而不必影响代码的执行流,能够提高代码的处理效率。 串行 vs 并发
串行指的是在同一个时间只能有一个任务在执行。 并发指的是在同一个时间可以有多个任务一起执行。
并发一般用于多核编程,通过高效的利用多核的优势,把不同的任务分配到各个执行单元来提高效率。 Dispatch Queue vs Operation
Queue
GCD和NSOperation/NSOperationQueue是iOS上面来处理多线程开发的工具,对应的概念分别是Dispatch
Queue和Operation Queue。
iOS并发编程模型
在其他的操作系统中,我们往往需要手动创建线程、管理线程的生命周期,在不需要的时候负责销毁线程和线程使用的资源,更加痛苦的是需要使用线程锁、信号量、代码临界区等手段完成线程的同步工作,这些操作往往容易出错而且繁杂。
iOS通过抽象出队列的概念,让开发者更加关注于任务的安排和调度,而从线程的管理工作中解脱出来。在很多时候,iOS把一些繁杂且容易出错的工作(ARC代替MRC)抽离到底层中,能够让开发者把注意力更多地放到真正的任务上,这也是iOS能够吸引广大开发者的原因之一吧。
NSThread vs GCD vs NSOperation,它们到底是什么?
三种解决方案
NSThread:一个封装pthread API的线程对象,需要进行线程创建、销毁和处理线程同步,是最接近系统底层的解决方案。
GCD:苹果基于C语言开发的,一个用于多核编程的解决方案,是一个轻量级的、以FIFO的顺序来执行并发任务的库。
NSOperation:建立在GCD的基础上,面向对象的解决方案,比GCD更加灵活,也更加强大。
它们具体是什么?
NSThread: Cocoa对于pthread
API的封装,提供了一套面向对象的接口,需要开发者自行管理线程的生命周期、处理线程同步。大多是的时候,我们是不需要直接使用这些底层的对象,而是使用GCD或者NSOperation等更加高级的接口。
关于Operation对象
NSOperation对象本身是一个抽象类,不能直接使用。要么使用系统预定义的两个子类NSInvocationOperation和NSBlockOperation或者定义它的子类。
NSInvocationOperation:可以使用object和selector来创建一个NSInvocationOperation,非常的方便和灵活。当代码中已经有相关的处理逻辑方法时,建议直接使用NSInvocationOperation来进行替代。
NSBlockOperation:可以使用NSBlockOperation来并发的执行一个或者多个block,只有当所有的block都执行完毕,NSBlockOperation才算执行完成,有点像dispatch_group的概念。
所有的Operation都有下面的特性:
支持在 operation 之间建立依赖关系,只有当一个 operation 所依赖的所有 operation 都执行完成时,这个 operation
才能开始执行; 支持一个可选的 completion block ,这个 block 将会在 operation
的主任务执行完成时被调用,在任务被取消的时候也会执行; 支持通过 KVO 来观察 operation
执行状态的变化,Operation正是通过KVO通知来实现依赖运行,所以我们需要在自定义的子类中的处理好KVO的属性; 支持设置执行的优先级,从而影响
operation 之间的相对执行顺序; 支持取消操作,可以允许我们停止正在执行的 operation 。 GCD队列:
以FIFO顺序执行任务的队列调度系统,先入队列的任务一定先执行。 两种类型的队列:
串行队列(Serial Queue):同一时间内只能有一个任务正在被执行。 并发队列(Concurrent
Queue):同一时间内可以有多个任务同时被执行。 iOS默认提供5个队列:
主队列(Main Queue):应用程序主线程应用的队列,用于更新UI,属于串行队列。 四个全局队列:
按照队列优先级排序,分别是:High、Default、Low、Background。
这四个全局队列是由系统提供的,在所有的App中共享,当然也包含了Apple的应用。 并发队列中任务的执行顺序:
由于队列的并发数是有系统根据当前的资源动态管理的,我们不知道也不能够设置队列中任务执行的时机和所需时长。
只有当位于队列前面的任务执行完毕、出队列后,才会执行后面的任务,但是当前并发执行的任务数量我们不得而知。 自定义队列:
我们可以自定义串行或者并发队列来完成任务的执行和调度。
各自的优势和劣势
优势:
NSThread:
能够执行实时任务。其他两者都是由系统管理的队列,不能保证实时性。 GCD:
可以非常简洁的完成简单异步任务的调用,如在主线程更新UI,延迟执行。 只需要把任务分发到队列之后,不需要管理任务的调度情况。
NSOperation:
给任务添加依赖 取消或者暂停一个正在执行的任务 有一个可选的completionBlock 可以通过KVO来查看任务的执行情况
可以给任务设置优先级,从而影响任务的执行顺序 短板:
NSThread:需要进行线程的创建、销毁,以及处理线程同步的问题,过于繁杂,而且容易出错。 GCD:
对于任务的管理不够:如不能取消任务、设置依赖和优先级等。 NSOperation:
相比于GCD,会增加系统的额外开销。
多线程代码中,需要注意哪些问题?
有了GCD来调度block,我们为什么还需要NSBlockOperation?
现有代码已经在使用OperationQueue,而我们不想使用Dispatch
Queue的时候,NSBlockOperation提供了一个面向对象的封装。 当我们需要dispatch
queue不能提供的功能时,如KVO观察Operation状态变化、设置operation之间依赖等。
NSInvocationOperation的灵活性是什么意思?
我们可以通过上下文来改变selector和object对象。 如何定义一个非并发的operation?
对于非并发的operation,我们只需要实现main方法和能够正常响应取消事件。
其他复杂的工作如KVO通知、依赖设置等工作NSOperation类的start方法已经帮我们提供了默认实现。 简单实现:
提供一个自定义的初始化方法 重写main方法 如何实现一个并发的operation?
一般我们不需要实现并发的operation,当operation和operation queue一起使用的时候,Operation
Queue会为非并发的operation创建单独的线程。
默认情况下,operation是同步执行的,也就是我们直接使用start方法的时候,它是在调用者的线程中执行的。
Operation的isConcurrent属性表明一个operation是否支持并发。
配置并发的Operation:只有当我们需要手动执行operation,并且希望支持并发执行,需要重写下面的方法。
start:必须,配置任务执行的线程和其他资源,但是一定不能调用父类的实现。
main:可选,一般用来执行具体的任务,而start方法更多是用来配置初始环境,当然也可以用来执行具体的任务。
isExecuting和isFinished:必须,并发的Operation需要自己来配置环境,同时还需要向外界来传递状态的变化,而isExecuting和isFinished这两个状态的变化需要使用KVO来通知外部。
isConcurrent:必须,用来标识一个Operation是否支持并发。 如何完成自定义operation的cancel操作?
在下面这几个关键点的检查isCancelled属性
在真正开始执行operaiton之前 至少在一次循环之中需要检查一次,如果单次循环耗时较长,则需要更加频繁的检查
在任何相对比较容易终止Operation的地方
需要注意的是虽然Operation支持取消操作,但是并不是立刻就可以被终止的,而是在下一个isCancelled的检查点。
在我们自定义Operation子类的时候,即使operation是被cancel了,我们仍然需要设置isFinished方法为true,因为在设置operation依赖的时候,它们的operation就是通过KVO来观察isFinished方法来判断时候可以执行的,如果在cancel的时候,没有设置isFinished方法,那么其他的operation将永远不会执行。
如何定制Operation对象的执行行为:
在Operation添加到Operation Queue之前,我们可以配置Operation的一些行为。 配置依赖关系
依赖关系是在Operation之间的,与是否在同一个Operation Queue没有关系,也就是说,我们在位于不同的Operation
Queue中的Operation之间设置依赖。 注意不要产生依赖循环。 在把operation添加到Operation
Queue之前就需要配置好依赖,在添加后设置的依赖可能无效。 修改operation在队列中的优先级
Operation在队列中的执行顺序取决于isReady状态和队列优先级。 isReady受它所依赖的operation状态的影响,只有当依赖的所有operation都变为isFinished的时候,isReady状态才为true。
queuePriority只能作用与相同队列中的operation,并且队列优先级只有当isReady为true的时候,才会决定operation的执行顺序。
isReady为FALSE的时候,isReady为true的低队列优先级的operation也会先执行。 一个串行的 operation queue
与一个串行的 dispatch queue是一样的么?
两者都是在同一时间内只能有一个任务被执行,但是在任务的执行顺序上是不同的:
dispatch queue 的执行顺序一直是 FIFO 的 operation
queue中的operation执行的顺序取决于isReady和queuePriority状态。 如何用GCD实现一个线程安全的单例?
本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之IOS频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号