iOS多线程NSThread,NSOperation和GCD详解

2022-08-03 15:00:39 浏览数 (2)

此文章已在我的论坛首发,论坛地址:

http://blog.csdn.net/shenjie12345678/article/details/51819493

线程是特别有用的,当你需要执行一个特别耗时的任务,但又不希望它阻塞程序的其余部分功能的执行。特别是,你可以使用线程来避免阻塞应用程序的主线程去处理用户界面的事件和有关的行动的功能。线程还可以用于将大型的工作划分成几个较小的部分,从而去提高设备的性能。

NSThread

NSThread是相对轻量级的多线程开发范式,但使用起来也是相对复杂,我们需要自己去管理线程的生命周期,线程之间的同步。

在iOS开发中我们可以用以下三种形式来实现NSThread:

运行效果如下:

当点击了按钮以后会启动一个新的线程,进行图片的下载,在这期间并不会去阻塞主线程的执行。当图片下载完成以后,会调用UI线程将图片显示到界面上去。

NSOperation&NSOperationQueue

NSOperation 是苹果公司对 GCD 的封装,NSOperation 只是一个抽象类,不能用于封装任务, 所以需要用它的子类NSInvocationOperation 和 NSBlockOperation来封装,两种方式没有本质的区别,但是后者使用Block的形式进行代码组织,在使用的过程中更加方便。

可以看到 NSOperation 和 NSOperationQueue 分别对应 GCD 的 任务 和 队列 。 操作步骤也很好理解: 1.将要执行的任务封装到一个 NSOperation 对象中。 2.将此任务添加到一个 NSOperationQueue 对列中,线程就会依次启动。

示例代码如下:

相比NSInvocationOperation推荐使用NSBlockOperation,因为它代码简单,同时由于闭包性使它没有传参问题,NSInvocationOperation在Swift中已不再支持。

运行效果如下:

自定义NSOperation,实现-(void)main函数,新开一个线程,实现图片异步下载.

代码如下:

以下代码为调用方式:

运行效果如下:

NSOperation依赖

当NSOperation对象需要依赖于其它NSOperation对象完成时再操作,就可以通过addDependency方法添加一个或者多个依赖的对象,只有所有依赖的对象都已经完成操作后,最开始的NSOperation对象才会开始执行,通过removeDependency来删除依赖对象。使用NSOperation进行多线程开发还可以设置最大并发线程,有效的对线程进行控制。

GCD

Grand Central Dispatch (GCD),它是为苹果多核的并行运算提出的解决方案,所以会自动合理的利用更多的CPU内核,更重要的是它会自动的管理线程的生命周期(创建线程,调度任务,销毁线程)。

在开始使用GCD的时候,需要搞清楚任务和队列这两个概念。 任务 有两种执行方式: 1.同步操作(sync),它会阻塞当前线程的操作并等待Block中的任务执行完毕,然后当前线程才会继续往下执行。 2.异步操作(async),当前线程会直接的往下执行,不会阻塞当前的线程。


队列 也有两种队列,串行队列与并行队列 串行队列:遵照先进先出的原则,取出来一个执行一个,创建串行队列时可用函数dispatch_queue_create来创建,其中第一个参数是标识符,第二个参数用于表示创建的队列是串行还是并行的,传入 DISPATCH_QUEUE_SERIAL 或 NULL 表示创建串行队列。

代码如下:

并行队列:也会遵照先进先出的原则,但不同的是它会将取出来的任务放到别的线程执行,然后再取出来一个放到另一个线程。创建并发队列时也可用函数dispatch_queue_create来创建,传入 DISPATCH_QUEUE_CONCURRENT 表示创建并行队列。但是在实际开发中我们会通过dispatch_get_global_queue()方法取得一个全局的并发队列,系统为每一个应用提供了3个并发队列,而且都是全局的,只是每个队列它们的优先级不同,分别是:

define DISPATCH_QUEUE_PRIORITY_HIGH 2 优先级最高

define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 优先级中等

define DISPATCH_QUEUE_PRIORITY_LOW (-2) 优先级最低

运行效果如下:

在GCD中还有一个特殊的队列———主队列,用来执行主线程上的操作,dispatch_get_main_queue() 它是全局可用的串行队列.

另外GCD还有其他任务执行方法: dispatch_group_async(队列组)的使用,队列组可以将很多队列添加到一个组里,这样做的好处是,当这个组里所有的任务都执行完了,队列组会通过dispatch_group_notify()方法获得完成通知。

dispatch_barrier_async():写入操作会确保队列前面的操作执行完毕才开始,并会阻塞队列中后来的操作.直到它执行完成后才会执行。

dispatch_apply():重复执行某个任务。

dispatch_once():单次执行一个任务,此方法中的任务只会执行一次,重复调用也没办法重复执行,单例模式中常用此方法。

dispatch_time():延迟一定的时间后执行。

可能上面的运行效果大家体会不到用多线程实现图片异步加载的效果,接下来我会在视图中加入6个UIImageView,分别开启6个线程来给UIImageView加载图片。

运行效果如下:

示例代码如下:

最后总结一下:

  1. 更新UI应该在主线程(UI线程)中进行.
  2. NSThread适合轻量级多线程开发,控制线程顺序比较难,同时线程总数无法控制.
  3. 使用NSThread的currentThread方法取得当前线程,使用 sleepForTimeInterval:方法让当前线程休眠.
  4. NSOperation进行多线程开发可以控制线程总数及线程依赖关系.
  5. 相比NSInvocationOperation推荐使用NSBlockOperation,代码简单,同时由于闭包性使它没有传参问题.
  6. NSOperation是对GCD面向对象的ObjC封装,但是相比GCD基于C语言开发,效率却更高,建议如果任务之间有依赖关系或者想要监听任务完成状态的情况下优先选择NSOperation否则使用GCD.
  7. 在GCD中串行队列中的任务被安排到一个单一线程执行(不是主线程),可以方便地控制执行顺序;并发队列在多个线程中执行(前提是使用异步方法),顺序控制相对复杂,但是更高效.
  8. 在GDC中一个操作是多线程执行还是单线程执行取决于当前队列类型和执行方法,只有队列类型为并行队列并且使用异步方法执行时才能在多个线程中执行(如果是并行队列使用同步方法调用则会在主线程中执行).

0 人点赞