MENU

Objective-C之GCD多线程(一)

February 9, 2017 • Read: 225 • iOS

前言

什么是GCD?

Grand Central dispatch(GCD)是异步执行任务的技术之一。一般将应用程序中记述的线程管理用的代码在系统级中实现。开发者只需要定义想执行的任务并追加到适当的dispatch Queue中,GCD就能生成必要的线程并计划执行任务。由于线程管理是作为系统的一部分来实现的,因此可以统一管理,也可执行任务,这样就比以前的线程更加有效率。

什么是线程?

  • 线程(thread)是操作系统能够进行运算调度的最小单位。
  • Mac、iPhone的操作系统OS X、iOS根据用户的指示启动应用程序后,首先便将包含在应用程序中的CPU命令列配置到内存中。CPU从应用程序指定的地址开始,一个一个地执行CPU命令列。由于一个CPU一次只能执行一个命令,不能执行某处分开的并列的两个命令,因此通过CPU执行的CPU命令列就是一条路走到黑。这里所说的“1个CPU执行的CPU名列为一条路走到黑”就是“线程”。
  • 现在一个物理的CPU芯片实际上有64(64核)个CPU,即可以拥有多条线程。
  • 多线程编程会产生很多编程技术问题:数据竞争、死锁、内存消耗等问题。
  • 想要更加了解线程、进程等可自行参阅《操作系统原理》这本书。

dispatch Queue

dispatch Queue是执行处理的等待队列。我们可以调用dispatch_async函数等API,在Block语法中记述想执行的处理并追加到dispatch Queue中。dispatch Queue会按照FIFO(先进先出)执行处理。下面的图解释了FIFO
{% asset_img Snip20170210_3.png 通过dispatch Queue执行处理%}
在dispatch Queue中存在两种dispatch Queue,一种是等待现在执行中的Serial dispatch Queue,另一种是不等待直接执行的Concurrent dispatch Queue。

dispatch Queue种类说明
Serial dispatch Queue等待现在执行中的处理结束后才开始执行
Concurrent dispatch Queue不等待现在执行中的处理,直接开始执行

Serial dispatch Queue

Concurrent dispatch Queue

因为Serial dispatch Queue是等待执行完成后才开始下一个处理。那么在有多个处理的时候,也是按照先后顺序来的。

int main(int argc, const char * argv[]) {
    // 创建一个SerialQueue
    dispatch_queue_t serialdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
    // Serial dispatch Queue
    dispatch_async(serialdispatchQueue, ^{printf("1\n");});
    dispatch_async(serialdispatchQueue, ^{printf("2\n");});
    dispatch_async(serialdispatchQueue, ^{printf("3\n");});
    dispatch_async(serialdispatchQueue, ^{printf("4\n");});
    // 防止程序结束
    while (1) {
    }
    return 0;
}

执行结果如下:

Serial Queue执行结果

Concurrent dispatch Queue是不等待执行,那么任何任务添加到Concurrent dispatch Queue后,就会立即执行。但是Concurrent dispatch Queue不是无限制的立即执行当前添加的处理,当前并行执行的处理的数量取决于当前系统的状态。即iOS和OS X基于dispatch Queue中的处理数、CPU数、以及CPU负荷等当前系统状态来决定Concurrent dispatch Queue中并行处理数。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    // 创建一个Concurrent dispatch Queue
    dispatch_queue_t concurrentdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_CONCURRENT);
    // Serial dispatch Queue
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"1");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"2");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"3");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"4");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"5");});
    dispatch_async(concurrentdispatchQueue, ^{NSLog(@"6");});
    // 防止程序结束
    while (1) {
    }
    return 0;
}

执行结果:

Concurrent dispatch Queue执行结果

其中执行结果乱序是因为处理添加到线程中的时候,需要等待线程执行完成后,才开始执行。即6个Block添加到6个线程中,6个线程里面都仍然有任务,等任务执行完成后,才开始执行我们添加的处理。

dispatch_queue_create

在dispatch Queue中,我们使用了dispatch_queue_create来建立一个Queue。
生成dispatch Queue有两种方法:

  • 通过GCD的API生成dispatch Queue
  • 获取系统标准提供的dispatch Queue
    这里先说明第一种方法,第二种在Main dispatch Queue/Global dispatch Queue中说明。

使用dispatch_queue_create

    // 创建一个Concurrent dispatch Queue
    dispatch_queue_t concurrentdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_CONCURRENT);

    // 创建一个Serial dispatch Queue
    dispatch_queue_t serialdispatchQueue = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);

dispatch_queue_create函数有2个参数,第一个参数是dispatch Queue的名称,推荐使用应用程序ID这样的逆序全程域名。该名称在Xcode和Instruments的调试器中作为dispatch Queue的名称表示。第二个参数是指定dispatch Queue的类型,如果填写NULL则默认为DISPATCH_QUEUE_SERIAL。
前文讲到,Serial dispatch Queue同时只能执行一个处理。但是生成多个Serial dispatch Queue时,各个Serial dispatch Queue将并行执行。4个处理追加到4个Serial dispatch Queue中,4个处理将同时执行。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    // 创建一个SerialQueue
    dispatch_queue_t serialdispatchQueue1 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialdispatchQueue2 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialdispatchQueue3 = dispatch_queue_create("com.larry.gcd.serialTest", DISPATCH_QUEUE_SERIAL);
    // Serial dispatch Queue
    dispatch_async(serialdispatchQueue1, ^{NSLog(@"1");});
    dispatch_async(serialdispatchQueue2, ^{NSLog(@"2");});
    dispatch_async(serialdispatchQueue3, ^{NSLog(@"3");});
    // 防止程序结束
    while (1) {
    }
    return 0;
}

执行结果:

多个Serial dispatch Queue同时执行

由执行结果乱序可以知道,多个Serial dispatch Queue是并行执行的!

dispatch_queue_create使用注意

  • 不能无限制的创建Serial Dispatch Queue,会消耗大量的内存,引起大量的上下文切换,大幅度降低系统的响应性能。
  • 在为了避免多个线程对同一个资源进行操作时(数据竞争)使用Serial Dispatch Queue,因为其使用一个线程,数据安全。
  • 当不需要顾忌数据竞争问题时候,推荐使用Concurrent Dispatch Queue。因为不管生成多少,系统会对其进行管理,不用担心Serial Dispatch Queue类似的问题。
  • 最好为每一个Dispatch Queue编写不同的名字,否则你会在调试多线程程序的时候感觉自己仿佛是一个辣鸡。
  • 关于 dispatch_retaindispatch_release 的使用

    • 如果你部署的最低目标低于 iOS 6.0 or Mac OS X 10.8,你应该自己管理GCD对象,使用(dispatch_retain,dispatch_release),ARC并不会去管理它们。
    • 如果你部署的最低目标是 iOS 6.0 or Mac OS X 10.8或者更高,ARC已经能够管理GCD对象了,这时候,GCD对象就如同普通的OC对象一样,不应该使用dispatch_retain或者dispatch_release。

Main Dispatch Queue/Global Dispatch Queue

除了使用dispatch_queue_create,还可以利用系统标准提供的Dispatch Queue。系统提供了2个:

  • Main Dispatch Queue,即主线程中执行的Dispatch Queue,而主线程只有一个,所以Main Dispatch Queue就是Serial Dispatch Queue.
  • Global Dispatch Queue,是所有应用程序都能够使用的Concurrent Dispatch Queue。所以没有必要通过dispatch_queue_create来创建,直接获取Global Dispatch Queue即可。
   /** Main Dispatch Queue的获取 */
   dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();

   /** Global Dispatch Queue(最高)的获取方法 */
   dispatch_queue_t highGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

   /** Global Dispatch Queue(默认)的获取方法 */
   dispatch_queue_t defaultGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

   /** Global Dispatch Queue(低)的获取方法 */
   dispatch_queue_t losGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

   /** Global Dispatch Queue(后台,最低)的获取方法 */
   dispatch_queue_t backgroundGlobalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

Global Dispatch Queue

Global Dispatch Queue有4个优先级,分别是高优先级、默认优先级、低优先级和后台优先级。

名称Dispatch Queue的种类说明
Main Dispatch QueueSerial Queue主线程执行
Global Dispatch Queue(High Priority)高优先级Concurrent Dispatch Queue执行优先级:最高
Global Dispatch Queue(Default Priority)默认优先级Concurrent Dispatch Queue执行优先级:默认
Global Dispatch Queue(Low Priority)低优先级Concurrent Dispatch Queue执行优先级:低
Global Dispatch Queue(Background Priority)后台优先级Concurrent Dispatch Queue执行优先级:后台(最低)

结语

  • 本文主要讲常用的一些GCD的API.
  • 此为《Objective-C 高级编程》的学习笔记。
  • 如有错误,欢迎指正。
  • 如需转载,请注明出处。
Tags: iOS开发
Archives QR Code
QR Code for this page
Tipping QR Code