iOS 多线程编程之GCD

来源:互联网 发布:java中tcp和udp的区别 编辑:程序博客网 时间:2024/05/18 19:46

三种多线程编程技术简介

iOS的三种多线程编程技术对比(这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的。):

  • NSThread
    • 优点:最轻量级
    • 缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
  • Cocoa NSOperation
    • 优点:不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。
  • GCD
    • 在iOS4.0开始之后才能使用。GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术

NSThread

NSThread已经有文章介绍得很详细,请看这里《iOS多线程编程之NSThread的使用》

NSOperation和NSOperationQueue

NSOperation和NSOperationQueue已经有文章介绍得很详细,请看这里《iOS多线程编程之NSOperation和NSOperationQueue的使用》

GCD

GCD全称Grand Central Dispatch,是苹果为多核的并行运算提出的解决方案,所以会自动合理地利用更多的CPU内核(比如双核、四核),最重要的是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,我们只需要告诉干什么就行。同时它使用的也是 c语言,不过由于使用了 Block(Swift里叫做闭包),使得使用起来更加方便,而且灵活。

1. 工作原理

GCD的工作原理是:让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。一个任务可以是一个函数(function)或者是一个block。 GCD的底层依然是用线程实现,不过这样可以让程序员不用关注实现的细节。
GCD中的FIFO队列称为dispatch queue,它可以保证先进来的任务先得到执行。

2.任务和队列

在 GCD 中,加入了两个非常重要的概念: 任务 和 队列。

  • 任务:即操作,你想要干什么,说白了就是一段代码,在 GCD 中就是一个 Block,所以添加任务十分方便。任务有两种执行方式: 同步执行 和 异步执行:
    • 同步(sync) 操作,它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行。
    • 异步(async)操作,当前线程会直接往下执行,它不会阻塞当前线程。它们主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕。
  • 队列:用于存放任务。一共有两种队列, 串行队列 和 并行队列:
    • 串行:放到串行队列的任务,GCD 会 FIFO(先进先出) 地取出来一个,执行一个,然后取下一个,这样一个一个的执行。
    • 并行:放到并行队列的任务,GCD 也会 FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。这样由于取的动作很快,忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。

这里写图片描述

3.分类

dispatch queue分为下面两种:

  • Serial
    又称为private dispatch queues,线性执行的线程队列,遵循FIFO(First In First Out)原则。Serial queue通常用于同步访问特定的资源或数据。当你创建多个Serial queue时,虽然它们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的。
dispatch_queue_t serialQueue = dispatch_queue_create("com.mark.serialQueue", NULL);dispatch_async(serialQueue, ^{    NSLog(@"block on serialQueue");});dispatch_release(serialQueue);
  • Concurrent
    又称为global dispatch queue,可以并发地执行多个任务,并发执行的处理数取决于当前状态,执行完成的顺序是随机的。
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.mark.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(concurrentQueue, ^{    NSLog(@"block on concurrentQueue");});dispatch_release(concurrentQueue);

系统的dispatch queue:

  • Main dispatch queue
    它是全局可用的serial queue,在程序的RunLoop中执行任务。

  • Global Dispatch Queue
    系统中所有应用程序共用的全局队列(Concurrent Queue), 一般不必创建新的Dispatch Queue,使用此Queue就可以了。有四个优先级:

DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND

4.实现

dispath_get_global_queue

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{      // 耗时的操作,例如下载网络图片     dispatch_async(dispatch_get_main_queue(), ^{          // 主线程操作,例如更新界面    });  });  

是不是代码比NSThread NSOperation简洁很多,而且GCD会自动根据任务在多核处理器上分配资源,优化程序。
系统给每一个应用程序提供了三个concurrent dispatch queues。这三个并发调度队列是全局的,它们只有优先级的不同。因为是全局的,我们不需要去创建。我们只需要通过使用函数dispath_get_global_queue去得到队列,如下:

dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    

这里线程的优先级是DISPATCH_QUEUE_PRIORITY_DEFAULT,若要改变线程优先级,用dispatch_set_target_queue:

// 改变serialQueue(Default Priority)优先级为Background Prioritydispatch_queue_t serialQueue = dispatch_queue_create("com.mark.serialQueue", NULL);dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);dispatch_set_target_queue(serialQueue, globalDispatchQueueBackground);

dispatch_get_main_queue

上面的例子中也用到了系统默认就有一个串行队列main_queue,它运行在程序的RunLopp中。

dispatch_queue_t mainQ = dispatch_get_main_queue();  

虽然dispatch queue是引用计数的对象,但是dispatch_get_global_queue和dispatch_get_main_queue都是全局的队列,不用retain或release。

dispatch_group_async

dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  dispatch_group_t group = dispatch_group_create();  dispatch_group_async(group, queue, ^{      [NSThread sleepForTimeInterval:1];      NSLog(@"group1");  });  dispatch_group_async(group, queue, ^{      [NSThread sleepForTimeInterval:2];      NSLog(@"group2");  });  dispatch_group_async(group, queue, ^{      [NSThread sleepForTimeInterval:3];      NSLog(@"group3");  });  dispatch_group_notify(group, dispatch_get_main_queue(), ^{      NSLog(@"updateUi");  });  dispatch_release(group); 

dispatch_group_async是异步的方法,运行后可以看到打印结果:

2015-11-25 17:47:09.625 NSThreadDemo[47595:12836120] group1
2015-11-25 17:47:10.624 NSThreadDemo[47595:12836122] group2
2015-11-25 17:47:11.620 NSThreadDemo[47595:12836651] group3
2015-11-25 17:47:11.620 NSThreadDemo[47595:12831653] updateUi

每个一秒打印一个,当第三个任务执行后,upadteUi被打印。

dispatch_barrier_async

dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行

dispatch_queue_t queue = dispatch_queue_create("gcdtest.rongfzh.yc", DISPATCH_QUEUE_CONCURRENT);  dispatch_async(queue, ^{      [NSThread sleepForTimeInterval:2];      NSLog(@"dispatch_async1");  });  dispatch_async(queue, ^{      [NSThread sleepForTimeInterval:4];      NSLog(@"dispatch_async2");  });  dispatch_barrier_async(queue, ^{      NSLog(@"dispatch_barrier_async");      [NSThread sleepForTimeInterval:4];  });  dispatch_async(queue, ^{      [NSThread sleepForTimeInterval:1];      NSLog(@"dispatch_async3");  });  

执行后打印结果:

2015-11-25 17:47:58.256 NSThreadDemo[47632:12844832] dispatch_async1
2015-11-25 17:48:00.259 NSThreadDemo[47632:12844831] dispatch_async2
2015-11-25 17:48:00.259 NSThreadDemo[47632:12844831] dispatch_barrier_async
2015-11-25 17:48:05.264 NSThreadDemo[47632:12844831] dispatch_async3

dispatch_apply

执行某个代码片段N次。

dispatch_apply(5, globalQ, ^(size_t index) {    // 执行5次});

dispatch_after

这个函数可以使线程延时执行:

// 2秒后将指定的Block增加到指定的Dispatch Queue中double delayInSeconds = 2.0;dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));dispatch_after(popTime, dispatch_get_main_queue(), ^(void){    NSLog(@"Waitted at least 2 seconds");});

线程队列的挂起与执行

// 挂起队列dispatch_suspend(queue);// 恢复队列执行dispatch_resume(queue);

dispatch_once

只执行一次制定处理的API

static dispatch_once_t pred;dispatch_once(&pred, ^{    // 初始化,这里多用于单例的模式});
0 0