objc系列译文(2.1):Objective-C并发编程:API和挑战

399 查看

并发指的是在同一时间运行多个任务。在单核CPU的情况下,它通过分时的方式实现,如果有多个CPU可用,则是真正意义上的多个任务“并行”执行了。

OS X和iOS提供了多个API支持并发编程。每个API都有自己特殊的功能和限制,适用于完成不同的任务。它们也分布在不同的抽象层次,我们可以通过底层API去做些非常接近硬件的底层操作,但这样的话,我们也需要做更多的事去保证一切运行正常。

并发编程是件非常棘手的事,有着许多复杂的问题和陷阱,而且在使用像GCD或NSOperationQueue这样的API时我们常常忘了这点。这篇文章将首先总体介绍OS X和iOS中不同的并发编程API,然后更深入地研究并发编程本身所固有的、与具体API无关的挑战。

 

OS X和iOS上的并发API

Apple的移动和桌面操作系统提供了相同的并发编程API。这篇文章中,我们来看一下pthread和NSThread、GCD、NSOperationQueue,以及NSRunLoop。从技术上讲,runloops被列在这里有点奇怪,因为他们并不支持真正的并发运行。之所以放在这里将,是因为他们和这个话题关系非常密切,值得我们了解一下。

我们将按从底层到上层的顺序来介绍这些API。之所以这样做是因为上层API都是建立在底层API基础上的。但当你实际使用时,你应该按照相反的顺序来选择:在能实现你的要求的前提下,尽量选上层API,这样能让你的并发模型更简洁。

如果你想知道为什么我们一直建议采用上层抽象和简洁的并发编程代码,你可以阅读这篇文章的第二部分:《并发编程所面临的挑战》,以及Peter Steinberger的这篇关于线程安全的文章

 

线程

线程是进程的子单元,可以单独被操作系统的调度器调度。事实上所有并发API都建立在线程基础上,包括GCD和操作队列。

多线程可以在单核CPU上同时运行(或至少看起来是在同时运行)。操作系统为每条线程分配运算时间片,这样用户感觉就好像多个任务同时被执行一样。如果CPU是多核的,多条线程就可以真正地被同时执行,完成某个操作的总体时间也因此能被缩短。

你可以通过Instruments的CPU 视图来了解你的代码或你所使用的框架代码在多核CPU上是怎样被调度的。

有一点需要牢记的是:你无法控制你的代码在何时何地被调度,以及它何时被暂停、暂停多久以供其他任务运行。线程的这种调度机制是个非常强大的技术,但同时也非常复杂,我们将在稍后对其进行说明。

暂时先抛开这种复杂性不谈,你可以使用POSIX线程API,或者Objective-C对此API进行的封装——NSThread,来创建你的线程。这里有个小例子,演示使用pthread来实现在一百万个数字中寻找最小值和最大值。它产生了4个并发运行的线程。从这个例子复杂的代码中可以很明显地看出为什么你不会希望直接使用pthread函数编程。