iOS并发开发简要整理(下)

上篇文章中简要介绍了iOS开发当中几种主要的并发实现方式,也重点整理了GCD和OperationQueue相关的内容。这篇就更进一步对比下GCD和Operation两种方式,同时整理下其它几个和并发相关的技术点,如NSThread、Runloop。

0. GCD & NSOperation

在“iOS并发开发简要整理(上)”里,已经详细地介绍了GCD和NSOperation(Queue)这两种实现并发的方式,这两者都摆脱了传统并发开发中“线程”概念的束缚,使用任务和队列的方式,在很多场景下规避了锁的使用,同时对线程的管理自动化了。

GCD和Operation都是在iOS平台上开发的推荐技术,从苹果的《Concurrency Programming Guide》中也可以看得出,这是苹果主推的并发开发方案。那么这两者又有什么关系,如何区别使用呢。

首先,NSOperation是上层技术,GCD则略偏底层,通过各种技术文档渠道我们可以了解到,NSOperation不过是面向对象化的任务-队列形式,是建立在GCD之上的。所以说,归根结底,这两者是一回事。

但是,既然GCD和Operation都是并发开发的主流方案,他们俩还是有所区别,各有长短的。

从GCD来看:

  • GCD是使用类C的方式实现的,相对偏底层,执行效率略高
  • GCD是用C语法形式的函数来做的,同时和Block紧密集合,对于熟悉、善于使用Block的开发者来说是非常不错的选择
  • 代码短小精悍、简单方便,轻量级,无需声明较复杂的类和对象

关于GCD的其实可以就说这么多,因为Operation技术方案是建立在GCD之上的,我们更多的还是要考量下NSOperation和NSOperationQueue:

  • 和GCD比,做了更上层的封装,虽然略牺牲了效率,但是他们是真正的Objective-C类,更适合在Objective-C的语境中出现
  • 支持面向对象,简单这么几个字可能说得太抽象,不过确实就是面向对象的一些优势,任务以对象的形式封装,可“重用”可复用,各Operation之间可以形成继承关系,同时具备封装、多态特性,有利于架构设计
  • 实现了任务间依赖关系管理
  • 支持取消
  • 具备KVO特性,随着任务状态变化,对应观察者可以收到通知
  • 很多人,包括苹果介绍Operation的Guide中,也还提到了优先级,但个人觉得这不是最吸引人的地方; Operation有2个优先级概念,一个是在队列中的优先级,一个是设置其潜在线程的优先级,前者不但与队列相关,也受依赖关系制约

基本上,对于Operation的优势所在,能想到的就是如上这些。这几个原因都是Operation相对GCD而言的,由于Operation也是基于GCD,所以用GCD肯定也都能做到,不过用Operation自然会更方便些,更合适些。

大家选用并发方案时,可按如上情况进行考量。

1. NSThread

说完了GCD和Operation这两个主流的方案,让我们回归源头,看一看NSThread。

我个人的感觉,这个和Java的java.lang.Thread很相似。让我们回想下Java的Thread怎么用? 对,用Runable对象传参构造或者继承Thread,然后start。

Objective-C的NSThread也是一样,通过target对象、实现方法的selector以及方法参数来初始化,原方法为:

- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument;

然后调用Thread对象的start。

还有比较简单的,通过类方法直接开启新线程去执行:

+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;

如是而已。

2. NSObject与线程

接触Objective-C时间比较长的朋友们都应该知道,我们Cocoa下的根类(之一),NSObject也有一些支持多线程的内容。

看NSObject.h的生命和NSThread.h里对NSObject的category扩展,我们会发现NSObject有一组performSelector方法。对于没有线程参数的performSelector来说,就只和当前线程有关系了。

这些performSelector和NSThread的初始化方法类似,通过selector来设定任务逻辑,有一点比较别扭的就是,参数个数有限,这个在实际开发当中用起来是非常不方便的,难怪这两种方案不是苹果主推的(当然还有其他原因)。

这里需要注意的一点是,在某个线程上想要performSelector的代码被执行,需要run loop开启。既然提到run loop,我们就来稍微详细地理一下,这也是本文的一个小重点。

3. RunLoop

Run loop是什么,直译为“运行循环”。没错,其实就是这个意思,我们在iPhone上的app,不是打开以下,过一段时间执行完代码就自动关闭,而是持久地运行着。这说明我们的代码不是一条直线,不是main运行一下就结束了,而有一个“循环”让它坚持地跑着,那就是Runloop。

这么说可能还是不知道Runloop是干嘛的,而且苹果的文档说得也不多。我这里多解释一句就是Runloop帮我们调度和处理程序运行中发生的各种“事件”。

先来看下NSRunLoop的几个运行的方法,然后再进一步说明。

- (void)run;
- (void)runUntilDate:(NSDate *)limitDate;
- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;

第一个方法的特点是,要么直接返回,要么很可能持久的运行下去(线程卡住了)。正如文档所说,它实际上是重复循环地调用第三个方法。

类似,第二个方法是如果没有任务就返回,要么也是死等执行直到limitDate,实际上也是重复调用第三个方法。

第三个方法……我们最后说。

刚刚提到了,run loop对象在run起来之后就去处理事件,执行任务。我们可以想象生活中一个死循环(比如while(YES){…}),里面的代码逻辑就是检查日历时间表,看看今天有没有安排,安排事情的事件有没有到,如果有或者时间到了,我们就马上去做某事。就这样,day after day,日历/时间表上上的安排也在随时在变化更新,有新的安排就记录到日程表上。直到某一天,自由日来临,不用继续做日程表上安排的任务了,或者明确知道日程表上不会再有事情了,到此为止。

这些任务安排,实际上就是iOS系统中的事件,苹果的文档解释为Timer & Input Source。而日复一日的检查日历表,就是Runloop的工作。

回到技术角度,我在上篇文章开头处就指出,多线程并发和IO的关系是很密切的,而run loop正是对包括IO事件做相应任务的一个异步化的处理机制。上一篇文章中也提到,对于一个线程来说,为了处理一个IO事件而阻塞,即使这个线程不是主线程,不会使得用户的交互行为得不到响应,但也是占用或者说浪费了系统资源的,不如释放这个线程,等待后续IO响应时重新开辟新线程处理任务。

相信很多人都知道select(我在Java的NIO相关文章里也提到过),而Run loop非常类似于循环地调用select,进而对感兴趣的事件进行处理。有所不同的是,我们在查看“日程表”的时候,如果发现当前没有安排的时候,不是一直死盯着日程表直到下一个安排时间点到来,我们可以趁这段事件睡个懒觉。这正是iOS在系统上对AIO这一理念的体现,也是run loop的优点所在,很好地节省系统资源。

此外,上述的第三个方法,是参数最全的一个方法,从参数中我们可以看到,run loop是分不同运行模式(mode)的,在一个模式下运行的run loop只会关注这个模式下的事件。

ASIHttpRequest这个框架中在网络请求的调度上,就很好地使用了run loop,感兴趣的朋友可以去参看下那段源代码。另外,如下这个网页也对run loop做了很好的讲解:

https://mikeash.com/pyblog/friday-qa-2010-01-01-nsrunloop-internals.html    

http://blog.shinetech.com/2009/06/02/run-loops-vs-threads-in-cocoa/

4. pthread

pthread即为POSIX thread,为支持POSIX标准的操作系统所支持。从实现来看,pthread是一套C语言的API和函数库,Objective-C是建立在C语言的基础之上的,支持C层面的开发,支持pthread。

对pthread感兴趣的,可以参看这里:

https://computing.llnl.gov/tutorials/pthreads/

5. 其它

关于iOS相关的并发技术,本文就先整理这些,下面是临时想到的几个需要注意的点,随手补在这里:

  • UI相关的一定要放在主线程处理,否则无效
  • dispatch_get_current_queue已经被deprecated,不建议再使用

相关文章:

此条目发表在 iOS, 开发, 计算机技术 分类目录,贴了 , , , , , , 标签。将固定链接加入收藏夹。

iOS并发开发简要整理(下)》有 1 条评论

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>