IOS开发入门之GCD使用以及多线程开发
白羽 2018-11-23 来源 :网络 阅读 433 评论 0

摘要:本文将带你了解IOS开发入门GCD使用以及多线程开发,希望本文对大家学IOS有所帮助。

    本文将带你了解IOS开发入门GCD使用以及多线程开发,希望本文对大家学IOS有所帮助。


        

对于iOS多线程开发,我们时刻处于学习之中,在看书中,看文档中,项目开发中,都可以去提高自己。最近刚看完了《Objective-C高级编程 iOS与OS X多线程和内存管理》这本书后,对多线程有了更为深入的理解,故在此做一个总结与记录。这本书是iOS开发者必读的书之一,写得很不错。书的封面如下,故也称狮子书:

(1)多线程会遇到的问题

多线程会出现什么问题呢?当多个线程对同一个数据进行修改的时候,会造成数据不一致的情况;当多个线程互相等待会造成死锁;过多的线程并发会大量消耗内存。所以多线程出现bug会严重影响App的功能和性能。

 

(2)多线程的作用

我们为什么一定要用多线程呢?只要有一个主线程不就可以了么。

 

从图中可以看到,如果我们有一个很耗时的操作放到主线程中执行,那么会严重阻塞主线程的执行,导致主界面不能及时响应用户的操作,出现卡死状态。当创建多线程之后,我们可以把耗时操作放到其他线程中去执行,比如网络操作,图片的上传下载等,当执行成功后再回到主线程更新界面即可。所以,使用多线程是必要的。

 

(3)GCD中的队列——Dispatch Queue

Dispatch Queue就是GCD中的调度队列,我们只要把任务添加到队列中去,线程就会按照顺序取出然后去执行,按照先进先出FIFO的原则。

 

 

(4)Dispatch Queue的种类

GCD中存在两种Dispatch Queue,一种是等待现在执行中处理的Serial Dispatch Queue(串行队列);另一种是不等待现在执行中处理的Concurrent Dispatch Queue(并发队列).

Serial Dispatch Queue:等待现在执行中处理结束;

Concurrent Dispatch Queue:不等待现在执行中处理结束,使用多个线程同时执行多个处理。

 

Serial Dispatch Queue为什么一定要等待处理结束才去执行下一个处理呢?因为Serial Dispatch Queue只有一个线程,一个线程同一时间当然只能执行一个任务,所以后续任务必须要进行等待。

Concurrent Dispatch Queue由于可以创建多个线程,所以只要按顺序取出任务即可,把取出的任务放到不同的线程去执行,任务之间是否执行结束没有必然关系,所以不需要等待前一个处理结束,看起来就像是多个任务同时在执行一样,其实也的确是在同时执行。当然这个并发数量系统会有限制,我们代码也可以设置最大并发数。

看了以上的解释,就知道Serial Dispatch Queue、Concurrent Dispatch Queue和线程的关系了,关系如下:

 

 

(5)多个Serial Dispatch Queue实现并发,以及遇到的问题

当生成多个Serial Dispatch Queue时,各个Serial Dispatch Queue将并行执行。虽然在一个Serial Dispatch Queue中同时只能执行一个追加处理,但是如果将处理分别追加到4个Serial Dispatch Queue中,各个Serial Dispatch Queue执行一个,即可以同时执行四个处理。

虽然这种笨办法也可以实现并发,但是也会遇到大问题,那就是消耗大量内存:

 

 

(6)资源竞争问题的解决

当多个线程对同一数据进行操作时可造成竞争或者数据不一致。最简单的解决办法就是使用Serial Dispatch Queue。Serial Dispatch Queue只创建一个线程,而一次只能执行一个任务,只有当该任务执行结束才能去执行下一个,所以同一时间对某个竞争资源的访问是唯一的。示意图如下:

 

(7)生成的Dispatch Queue必须由程序员释放。这是因为Dispatch Queue并没有像Block那样具有作为OC对象来处理的技术。通过dispatch_queue_create函数生成的Dispatch Queue在使用结束后通过dispatch_release释放。看个下面的例子:

这样立即释放queue是否有问题呢?

在dispatch_async函数中追加Block到Dispatch Queue后,即使立即释放Dispatch Queue,该Dispatch Queue由于被Block持有也不会被废弃,因而Block能够执行。Block执行结束后会释放Dispatch Queue,这时谁都不持有Dispatch Queue,因此它会被废弃。

 

(8)系统标准提供的Dispatch Queue

-- Main Dispatch Queue:在主线程中执行的queue,因为主线程只有一个,所以Main Dispatch Queue自然就是Serial Dispatch Queue。追加到Main Dispatch Queue的处理在主线程的RunLoop中执行。

 

 

-- Global Dispatch Queue:是所有应用程序都能够使用的Concurrent Dispatch Queue,没有必要通过dispatch_queue_create函数逐个生成Concurrent Dispatch Queue,只要获取Global Dispatch Queue即可。其中有四个优先级,但是用于Global Dispatch Queue的线程并不能保证实时性,因此执行优先级只是大致的判断。

对于Main Dispatch Queue和Global Dispatch Queue执行dispatch_retain函数和dispatch_release函数不会引起任何变化,也不会有任何问题。这也是获取并使用Global Dispatch Queue比生成、使用、释放Concurrent Dispatch Queue更轻松的原因。

 

 

(9)dispatch_set_target_queue:改变生成的Dispatch Queue的执行优先级

指定要变更执行优先级的Dispatch Queue为dispatch_set_target_queue函数的第一个参数,指定与要使用的执行优先级相同的Global Dispatch Queue为第二个参数(目标),第一个参数如果指定系统提供的Main Dispatch Queue和Global Dispatch Queue,则不会知道出现什么状态,因此这些均不可指定。

 

(10)dispatch_after:延迟执行

NSEC_PER_SEC:秒

NSEC_PER_MSEC:毫秒

 

(11)dispatch_barrier_async

会等待追加到Concurrent Dispatch Queue上的并行执行的处理全部结束之后,再将指定的处理追加到该Concurrent Dispatch Queue中。然后在由dispatch_barrier_async函数追加的处理执行完毕后,Concurrent Dispatch Queue才恢复为一般的动作,追加到该Concurrent Dispatch Queue的处理又开始并行执行。示意图如下:

..

 

(12)dispatch_async

将指定的block“非同步”的追加到指定的Dispatch Queue中,dispatch_async函数不做任何等待。

 

 

(13)dispatch_sync造成的问题

一旦调用dispatch_sync函数,那么在指定的处理执行结束之前,该函数不会返回。但是dispatch_sync容易造成死锁。

该源代码在Main Dispatch Queue即主线程中执行指定的Block,并等待其执行结束。而其实在主线程中正在执行这些源代码,所以无法执行追加到Main Dispatch Queue的Block。下面的例子也一样:

Main Dispatch Queue中执行的Block等待Main Dispatch Queue中要执行的Block执行结束。当然Serial Dispatch Queue也会引起相同的问题。

 

(14)dispatch_apply

dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API。该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等到全部处理结束。

 

因为在Global Dispatch Queue中执行处理,所以各个处理的执行时间不定,是不进行等待的追加任务。但是输出结果中最后的done必定在最后的位置上。这是因为dispatch_apply函数会等待全部处理执行结束。

由于dispatch_apply函数也与dispatch_sync函数相同,会等待处理执行结束,因此推荐在dispatch_async函数中非同步的执行dispatch_apply函数。

 

 

 

(15)dispatch_suspend/dispatch_resume

当追加大量处理到Dispatch Queue时,在追加处理的过程中,有时希望不执行已追加的处理。在这种情况下,只要挂起Dispatch Queue即可。当可以执行时再恢复。

-- dispatch_suspend函数挂起指定的Dispatch Queue:

dispatch_suspend(queue);

 

dispatch_resume函数恢复指定的Dispatch Queue:

dispatch_resume(queue);

 

这些函数对已经执行的处理没有影响。挂起后,追加到Dispatch Queue中但尚未执行的处理在此之后停止执行。而恢复则使得这些处理能够继续执行。

 

(16)Dispatch Semaphore

当不使用信号量的时候出现的bug.

.

这里使用Global Dispatch Queue更新NSMutableArray类对象,所以执行后由内存错误导致应用程序异常结束的概率很高。

Dispatch Semaphore是持有计数信号,该计数是多线程编程中的计数类型信号。计数为0时等待,计数为1或者大于1时,减去1而不等待。

创建semaphore:

 

参数表示计数的初始值。

dispatch_semaphore_wait函数等待Dispatch Semaphore的计数值达到或者等于1.当计数值大于等于1,或者在等待中计数值大于等于1时,对该计数进行减法并从dispatch_semaphore_wait函数返回。

semaphore可以进行以下分支处理:

 

dispatch_semaphore_wait函数返回0时,可安全的执行需要进行排他控制的处理。该处理结束时通过dispatch_semaphore_signal函数将Dispatch Semaphore的计数数值加1.

案例:

 

 

(17)dispatch_once

dispatch_once函数是保证在应用程序执行中只执行一次指定处理的API。下面这种经常出现的用来初始化的源代码可通过dispatch_once函数简化:

 

在多核CPU中,在正在更新表示是否初始化的标志变量时读取,就有可能多次执行初始化处理。而用dispatch_once函数初始化就不用担心了。这就是单例模式,在生成单例对象时使用。

 

(18)GCD的基本实现与描述

苹果官方说明:通常,应用程序中编写的线程管理用的代码要在系统级实现。

什么是系统级实现?就是在iOS和macOS的核心XNU内核级上实现。因此,无论程序员如何努力编写管理线程的代码,在性能方面也不可能胜过XNU内核级所实现的GCD。

用于实现Dispatch Queue而使用的软件组件:

Dispatch Queue没有“取消”这一概念。一旦将处理追加到Dispatch Queue中,就没有办法可以将该处理删除,也没有办法就执行中取消该处理。

          

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之IOS频道!


本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程