Objective-C之GCD多线程(一)

作者: Larrycal | 来源:发表于2017-02-13 22:09 被阅读727次

    前言

    什么是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

    通过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 Queue Serial 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 高级编程》的学习笔记。
    • 如有错误,欢迎指正。
    • 如需转载,请注明出处。

    相关文章

      网友评论

        本文标题:Objective-C之GCD多线程(一)

        本文链接:https://www.haomeiwen.com/subject/jlitwttx.html