深入浅出GCD —— 所学即所用

作者: EmptyWalker | 来源:发表于2017-01-10 23:25 被阅读240次

    CGD简介

    什么是GCD

    • GCD的全称是Grand Central Dispatch,可以把它翻译为"牛逼的中枢调度器"。😉
    • 纯C语言,提供了很多强大的函数

    GCD的优势

    • GCD是苹果公司为解决多核的并行运算而提出的解决方案
    • CGD会自动利用更多的CPU内容(如双核、四核...)
    • GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
    • 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

    GCD中的概念和使用步骤

    2个核心概念

    任务:

    执行的什么操作,用block代码块的形式创建

    队列:

    是用来陈放任务的载体,负责调度任务;队列有两种类型:

    • 串行队列:一个接一个的调度任务 (DISPATCH_QUEUE_SERIALdispatch_get_main_queue())
    • 并行队列:可以同时调度多个任务 (DISPATCH_QUEUE_CONCURRENTdispatch_get_global_queue(0,0))

    使用步骤(2步)

    定制任务

    确定想要执行的任务

    将任务添加到队列中

    • GCD会自动将队列中的任务取出,放到对应的线程中执行
    • 任务的取出遵循队列的FIFO原则:先进先出

    在这个过程中,我们需要做的就是:将任务以指定的方式(同步/异步)添加到队列中,队列就会按照我们指定的方式调度任务。

    • 同步:一个任务没有结束,就不会执行下一个任务
    • 异步:不用等当前任务执行完毕,就会去线程池开辟一个子线程,执行下一个任务
    • 线程池:在GCD的底层有一个线程池,负责创建线程、分配线程、销毁线程

    我的理解

    队列、任务、线程池

    如上图,GCD主要由任务、队列、执行方式三个部分组成,又由于队列类型(串行/并行)、执行方式(同步/异步)的不同,可以两两组合,从而出现四种情况,后面会一一通过代码来实现。这里主要说明的是:

    • 对于串行队列
      当任务执行时,必须要等当前任务执行完成后才能去队列中拿下一个任务,所以不管是同步执行,还是异步执行,都是按照顺序执行的

    • 对于并行队列
      当任务执行时,不用等当前任务执行完毕就可以去队列拿下一个任务,此时若是:

      • 同步执行
        由于只有同步执行不会去现成池拿新的线程,即使你拿到多个任务,也只能在当前线程排队等待,等前一个任务执行完毕后,才能执行下一个,所以还是按照顺序执行
      • 异步执行
        如果在并行队列异步执行任务的话,首先会去队列中拿到多个任务,并且会去线程池中获取子线程,所以会出现多个任务并发执行的情况,这时候任务的执行顺序是不确定的,而我们用GCD大部分的场景都是这种情况

    下面,通过代码,来依次验证这些情况。

    GCD实战

    创建队列

    所有的队列都是dispatch_queue_t类型,常用创建队列通常有三种方法,其中有两种是系统提供的:

    • 主队列
      通过 dispatch_get_main_queue()方法返回一个dispatch_queue_t对象,这是一个串行队列,即该队列中的所有任务都是顺序执行的。

    • 全局队列
      这个队列通过dispatch_get_global_queue(long identifier, unsigned long flags)函数获取,对于这个函数有两个参数:

      • identifier
        这个参数是表示一个优先级的,但是在iOS 8之前和之后其参数类型不太一样
        iOS 8及之后版本表示的含义是:服务质量,其类型如下:
         QOS_CLASS_USER_INTERACTIVE    用户交互(希望线程快速被执行,不要用好使的操作)
         QOS_CLASS_USER_INITIATED      用户需要的(同样不要使用耗时操作)
         QOS_CLASS_DEFAULT             默认的(给系统来重置队列的)
         QOS_CLASS_UTILITY             使用工具(用来做耗时操作)
         QOS_CLASS_BACKGROUND          后台
         QOS_CLASS_UNSPECIFIED         没有指定优先级
    

    而在iOS 8之前版本表示的含义是:调度的优先级,其类型如下:

         DISPATCH_QUEUE_PRIORITY_HIGH 2               高优先级
         DISPATCH_QUEUE_PRIORITY_DEFAULT 0            默认优先级
         DISPATCH_QUEUE_PRIORITY_LOW (-2)             低优先级
         DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
    

    提示

    不要选择BACKGROUND 优先级,服务质量,因为线程执行会慢到令人发指!!!体现在任务调度次数少,执行时间短。通常基本都使用默认的,填入0

      • flags: 是一个预留参数,同常填0
    • 自己创建一个队列
      GCD提供了一个可以自己创建队列的函数:dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t _Nullable attr#>)函数,使用此函数可以创建一个自定义的队列。
      • label参数: 是一个队列标签,即队列名,通常给一个字符串
      • attr参数: 是队列属性;系统提供了两个宏:DISPATCH_QUEUE_SERIAL或者NULL因为这个宏就是NULL,表示串行队列; DISPATCH_QUEUE_CONCURRENT:表示并行队列。

    添加任务

    添加任务:将任务以指定的执行方式添加到队列中去;它的方式有两种:

    • 同步执行:
      即只会在当前线程中执行任务,无法去线程池中获取新的线程
        /**
        *queue: 添加到的队列
        *block:就放我们需要执行的任务
        */
        dispatch_sync(dispatch_queue_t  _Nonnull queue, ^{
            //you todo 
        });
    
    • 异步执行:
      即可以去线程池中获取新的线程,其参数含义和同步执行的相同,只是函数名换成了dispatch_async
        dispatch_async(dispatch_queue_t  _Nonnull queue, ^{
        
            //you todo
        }) 
    

    介绍完了基本函数语法,下面来用代码实现上述所说的4中情况。

    串行队列同步执行

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        
        [self gcdBase1];
        
    }
    
    - (void)gcdBase1 {
    
        dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_SERIAL);
        for (NSInteger i = 0; i < 10; i ++) {
            dispatch_sync(queue, ^{
                NSLog(@"%ld ------ %@", (long)i,  [NSThread currentThread]);
            });
        }
    }
    

    我在touchesBegan方法中,串行队列中以同步执行的方法添加了10个NSLog任务,当点击屏幕时,控制台获得以下输出:

    
    2017-01-10 21:54:58.609 GCD-01[13124:1295897] 0 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
    2017-01-10 21:54:58.609 GCD-01[13124:1295897] 1 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
    2017-01-10 21:54:58.609 GCD-01[13124:1295897] 2 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
    2017-01-10 21:54:58.610 GCD-01[13124:1295897] 3 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
    2017-01-10 21:54:58.610 GCD-01[13124:1295897] 4 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
    2017-01-10 21:54:58.610 GCD-01[13124:1295897] 5 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
    2017-01-10 21:54:58.610 GCD-01[13124:1295897] 6 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
    2017-01-10 21:54:58.611 GCD-01[13124:1295897] 7 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
    2017-01-10 21:54:58.611 GCD-01[13124:1295897] 8 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
    2017-01-10 21:54:58.611 GCD-01[13124:1295897] 9 ------ <NSThread: 0x600000079e00>{number = 1, name = main}
    
    

    由结果可以看出,不会开启线程,并且任务是顺序执行的,符合预期,并且无论你点击多少次,结果都不会改变。

    串行队列异步执行

    这次我在touchesBegan方法中执行以下任务,在一个串行队列中,添加10个异步任务,并且在任务添加后,打印一句I am here,猜猜这次的打印结果会是怎么样的?????

    - (void)gcdBase2 {
        
        dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_SERIAL);
        for (NSInteger i = 0; i < 10; i ++) {
            dispatch_async(queue, ^{
                NSLog(@"%ld ------ %@", (long)i,  [NSThread currentThread]);
            });
        }
        NSLog(@"I am here");
    }
    

    在模拟器中点击屏幕,获得以下结果:

    2017-01-10 22:06:01.703 GCD-01[13385:1322583] I am here <NSThread: 0x600000066a40>{number = 1, name = main}
    2017-01-10 22:06:01.703 GCD-01[13385:1322734] 0 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
    2017-01-10 22:06:01.703 GCD-01[13385:1322734] 1 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
    2017-01-10 22:06:01.703 GCD-01[13385:1322734] 2 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
    2017-01-10 22:06:01.703 GCD-01[13385:1322734] 3 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
    2017-01-10 22:06:01.704 GCD-01[13385:1322734] 4 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
    2017-01-10 22:06:01.704 GCD-01[13385:1322734] 5 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
    2017-01-10 22:06:01.704 GCD-01[13385:1322734] 6 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
    2017-01-10 22:06:01.705 GCD-01[13385:1322734] 7 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
    2017-01-10 22:06:01.705 GCD-01[13385:1322734] 8 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
    2017-01-10 22:06:01.705 GCD-01[13385:1322734] 9 ------ <NSThread: 0x61800006b0c0>{number = 3, name = (null)}
    

    由以上结果可以看出,异步执行,会创建新的线程去执行任务,在串行队列中,由于每次只能拿一个任务,所以任务是按照FIFO顺序执行的。这里的I am here位置不一定一直处于第一位,但是肯定是靠前的。但是我试了很多次,都是第一位,尴尬。。。

    并行队列同步执行

    我在touchesBegan方法中执行以下任务;将队列类型换成DISPATCH_QUEUE_CONCURRENT,即并行队列,然后添加同步任务

    - (void)gcdBase3 {
        
        dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_CONCURRENT);
        for (NSInteger i = 0; i < 10; i ++) {
            dispatch_sync(queue, ^{
                NSLog(@"%ld ------ %@", (long)i,  [NSThread currentThread]);
            });
        }
        NSLog(@"I am here %@", [NSThread currentThread]);
    }
    

    点击屏幕获取打印:

    2017-01-10 22:12:34.551 GCD-01[13537:1337073] 0 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
    2017-01-10 22:12:34.551 GCD-01[13537:1337073] 1 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
    2017-01-10 22:12:34.551 GCD-01[13537:1337073] 2 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
    2017-01-10 22:12:34.552 GCD-01[13537:1337073] 3 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
    2017-01-10 22:12:34.552 GCD-01[13537:1337073] 4 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
    2017-01-10 22:12:34.552 GCD-01[13537:1337073] 5 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
    2017-01-10 22:12:34.552 GCD-01[13537:1337073] 6 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
    2017-01-10 22:12:34.553 GCD-01[13537:1337073] 7 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
    2017-01-10 22:12:34.553 GCD-01[13537:1337073] 8 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
    2017-01-10 22:12:34.553 GCD-01[13537:1337073] 9 ------ <NSThread: 0x6180000764c0>{number = 1, name = main}
    2017-01-10 22:12:34.553 GCD-01[13537:1337073] I am here <NSThread: 0x6180000764c0>{number = 1, name = main}
    

    结果:由打印信息可以看出,同步任务不会创建新的线程,由于只有一个线程,即使是并行队列可以同时获取多个任务,最终也会按照顺序执行,因为只有一条线程。

    并行队列异步执行

    - (void)gcdBase4 {
        
        dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_CONCURRENT);
        for (NSInteger i = 0; i < 10; i ++) {
            dispatch_async(queue, ^{
                NSLog(@"%ld ------ %@", (long)i,  [NSThread currentThread]);
            });
        }
        NSLog(@"I am here %@", [NSThread currentThread]);
    }
    

    这次我多次点击屏幕时,获得以下输出:

    2017-01-10 22:18:15.768 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
    2017-01-10 22:18:15.768 GCD-01[13664:1350805] 0 ------ <NSThread: 0x618000070700>{number = 3, name = (null)}
    2017-01-10 22:18:15.768 GCD-01[13664:1351252] 1 ------ <NSThread: 0x61800006bf80>{number = 4, name = (null)}
    2017-01-10 22:18:15.768 GCD-01[13664:1351253] 2 ------ <NSThread: 0x610000073880>{number = 5, name = (null)}
    2017-01-10 22:18:15.768 GCD-01[13664:1351254] 3 ------ <NSThread: 0x61800006afc0>{number = 6, name = (null)}
    2017-01-10 22:18:15.768 GCD-01[13664:1351255] 4 ------ <NSThread: 0x6000000725c0>{number = 7, name = (null)}
    2017-01-10 22:18:15.768 GCD-01[13664:1351256] 5 ------ <NSThread: 0x61800006a2c0>{number = 8, name = (null)}
    2017-01-10 22:18:15.769 GCD-01[13664:1351257] 6 ------ <NSThread: 0x610000073540>{number = 9, name = (null)}
    2017-01-10 22:18:15.769 GCD-01[13664:1351258] 7 ------ <NSThread: 0x618000070480>{number = 10, name = (null)}
    2017-01-10 22:18:15.769 GCD-01[13664:1350805] 8 ------ <NSThread: 0x618000070700>{number = 3, name = (null)}
    2017-01-10 22:18:15.769 GCD-01[13664:1351252] 9 ------ <NSThread: 0x61800006bf80>{number = 4, name = (null)}
    
    
    2017-01-10 22:18:24.114 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
    2017-01-10 22:18:24.115 GCD-01[13664:1351637] 1 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
    2017-01-10 22:18:24.115 GCD-01[13664:1351259] 0 ------ <NSThread: 0x60800006e080>{number = 11, name = (null)}
    2017-01-10 22:18:24.115 GCD-01[13664:1351638] 2 ------ <NSThread: 0x6180000705c0>{number = 13, name = (null)}
    2017-01-10 22:18:24.115 GCD-01[13664:1351639] 3 ------ <NSThread: 0x61000006f240>{number = 14, name = (null)}
    2017-01-10 22:18:24.115 GCD-01[13664:1351641] 4 ------ <NSThread: 0x61800006c080>{number = 15, name = (null)}
    2017-01-10 22:18:24.115 GCD-01[13664:1351643] 5 ------ <NSThread: 0x600000071f80>{number = 16, name = (null)}
    2017-01-10 22:18:24.115 GCD-01[13664:1351640] 7 ------ <NSThread: 0x600000072ac0>{number = 18, name = (null)}
    2017-01-10 22:18:24.115 GCD-01[13664:1351644] 6 ------ <NSThread: 0x618000070500>{number = 17, name = (null)}
    2017-01-10 22:18:24.115 GCD-01[13664:1351637] 8 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
    2017-01-10 22:18:24.115 GCD-01[13664:1351647] 9 ------ <NSThread: 0x610000073540>{number = 19, name = (null)}
    
    
    2017-01-10 22:18:26.446 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
    2017-01-10 22:18:26.446 GCD-01[13664:1351647] 0 ------ <NSThread: 0x610000073540>{number = 19, name = (null)}
    2017-01-10 22:18:26.446 GCD-01[13664:1351637] 1 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
    2017-01-10 22:18:26.446 GCD-01[13664:1351644] 2 ------ <NSThread: 0x618000070500>{number = 17, name = (null)}
    2017-01-10 22:18:26.446 GCD-01[13664:1351640] 3 ------ <NSThread: 0x600000072ac0>{number = 18, name = (null)}
    2017-01-10 22:18:26.447 GCD-01[13664:1351643] 4 ------ <NSThread: 0x600000071f80>{number = 16, name = (null)}
    2017-01-10 22:18:26.447 GCD-01[13664:1351641] 5 ------ <NSThread: 0x61800006c080>{number = 15, name = (null)}
    2017-01-10 22:18:26.447 GCD-01[13664:1351639] 6 ------ <NSThread: 0x61000006f240>{number = 14, name = (null)}
    2017-01-10 22:18:26.447 GCD-01[13664:1351638] 7 ------ <NSThread: 0x6180000705c0>{number = 13, name = (null)}
    2017-01-10 22:18:26.447 GCD-01[13664:1351259] 8 ------ <NSThread: 0x60800006e080>{number = 11, name = (null)}
    2017-01-10 22:18:26.447 GCD-01[13664:1351646] 9 ------ <NSThread: 0x610000073d80>{number = 20, name = (null)}
    
    
    
    2017-01-10 22:18:26.598 GCD-01[13664:1351646] 0 ------ <NSThread: 0x610000073d80>{number = 20, name = (null)}
    2017-01-10 22:18:26.598 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
    2017-01-10 22:18:26.598 GCD-01[13664:1351259] 1 ------ <NSThread: 0x60800006e080>{number = 11, name = (null)}
    2017-01-10 22:18:26.598 GCD-01[13664:1351638] 2 ------ <NSThread: 0x6180000705c0>{number = 13, name = (null)}
    2017-01-10 22:18:26.598 GCD-01[13664:1351639] 3 ------ <NSThread: 0x61000006f240>{number = 14, name = (null)}
    2017-01-10 22:18:26.598 GCD-01[13664:1351641] 4 ------ <NSThread: 0x61800006c080>{number = 15, name = (null)}
    2017-01-10 22:18:26.598 GCD-01[13664:1351643] 5 ------ <NSThread: 0x600000071f80>{number = 16, name = (null)}
    2017-01-10 22:18:26.598 GCD-01[13664:1351640] 6 ------ <NSThread: 0x600000072ac0>{number = 18, name = (null)}
    2017-01-10 22:18:26.598 GCD-01[13664:1351644] 7 ------ <NSThread: 0x618000070500>{number = 17, name = (null)}
    2017-01-10 22:18:26.598 GCD-01[13664:1351637] 8 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
    2017-01-10 22:18:26.599 GCD-01[13664:1351649] 9 ------ <NSThread: 0x61800006f680>{number = 21, name = (null)}
    
    
    
    2017-01-10 22:18:26.734 GCD-01[13664:1351637] 1 ------ <NSThread: 0x610000073940>{number = 12, name = (null)}
    2017-01-10 22:18:26.734 GCD-01[13664:1351649] 0 ------ <NSThread: 0x61800006f680>{number = 21, name = (null)}
    2017-01-10 22:18:26.734 GCD-01[13664:1351644] 2 ------ <NSThread: 0x618000070500>{number = 17, name = (null)}
    2017-01-10 22:18:26.734 GCD-01[13664:1350605] I am here <NSThread: 0x60000006bcc0>{number = 1, name = main}
    2017-01-10 22:18:26.734 GCD-01[13664:1351640] 3 ------ <NSThread: 0x600000072ac0>{number = 18, name = (null)}
    2017-01-10 22:18:26.734 GCD-01[13664:1351643] 4 ------ <NSThread: 0x600000071f80>{number = 16, name = (null)}
    2017-01-10 22:18:26.734 GCD-01[13664:1351641] 5 ------ <NSThread: 0x61800006c080>{number = 15, name = (null)}
    2017-01-10 22:18:26.734 GCD-01[13664:1351639] 6 ------ <NSThread: 0x61000006f240>{number = 14, name = (null)}
    2017-01-10 22:18:26.734 GCD-01[13664:1351638] 7 ------ <NSThread: 0x6180000705c0>{number = 13, name = (null)}
    2017-01-10 22:18:26.734 GCD-01[13664:1351259] 8 ------ <NSThread: 0x60800006e080>{number = 11, name = (null)}
    2017-01-10 22:18:26.735 GCD-01[13664:1351646] 9 ------ <NSThread: 0x610000073d80>{number = 20, name = (null)}
    

    结果分析: 由多次点击结果可以确定,异步任务,开启了新线程,并发执行能够拿到多个任务,所以上面会出现任务执行顺序不一致,并且I am here的位置,也可以确定主线程没有被影响。

    一个模拟项目中的同步任务

    在这里我模拟一个实际开发流程:比如在一个读书的APP中,通常会出现付费下载的情况,当用户在游客模式去浏览,发现到自己喜欢的书籍时,去点击下载,这时候就需要走 登录 -> 支付 -> 下载,这就是一个同步任务,这里有很多网络请求,属于耗时操作,通常我们都在子线程中去完成。在这个任务中,我们将设置一下流程,用户必须先登录,然后同时执行支付和下载任务。对此,实现以下代码:

    - (void)gcdSyncTask {
        //创建一个并行队列
        dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_CONCURRENT);
        //同步添加 用户登录 任务
        dispatch_sync(queue, ^{
            NSLog(@"用户登录");
        });
        //异步添加 支付任务 可以在子线程中执行
        dispatch_async(queue, ^{
            NSLog(@"支付任务");
        });
        //异步添加 下载任务 可以在子线程中执行
        dispatch_async(queue, ^{
            NSLog(@"下载任务");
        });
    }
    

    当点击屏幕获得以下打印:经过多次点击都可以发现,用户登录永远都先执行的,只有用户登录完成后,才会去执行支付和下载任务。实际情况,任务的执行会更加耗时,那么支付和下载的完成顺序也就会不确定

    2017-01-10 22:50:53.816 GCD-01[14340:1423863] 用户登录
    2017-01-10 22:50:53.816 GCD-01[14340:1423979] 支付任务
    2017-01-10 22:50:53.816 GCD-01[14340:1423977] 下载任务
    

    对模拟项目升级

    在之前的项目中,所以的点击添加任务都是在主线程中添加的,下面模拟一下在,子线程中添加上述的同步任务会出现什么情况:

    - (void)gcdStrongSyncTask {
        //创建一个并行队列
        dispatch_queue_t queue = dispatch_queue_create(myQueue, DISPATCH_QUEUE_CONCURRENT);
        
        //这里讲之前的任务 放到task代码快中
        void (^task)() = ^{
            
            for (NSInteger i = 0; i < 10; i ++) {
                dispatch_sync(queue, ^{
                    NSLog(@"%ld ------ %@", (long)i,  [NSThread currentThread]);
                });
            }
            //同步添加 用户登录 任务
            dispatch_sync(queue, ^{
                NSLog(@"用户登录  %@", [NSThread currentThread]);
            });
            //异步添加 支付任务 可以在子线程中执行
            dispatch_async(queue, ^{
                NSLog(@"支付任务   %@", [NSThread currentThread]);
            });
            //异步添加 下载任务 可以在子线程中执行
            dispatch_async(queue, ^{
                NSLog(@"下载任务  %@", [NSThread currentThread]);
            });
        };
        //以异步任务的方式,将task在子线程中去执行,从而完成在子线程中去执行之前的任务
        dispatch_async(queue, task);
        NSLog(@" I come here    %@", [NSThread currentThread]);
    }
    

    以下结果是我多次点击之后得到的,我选取其中有代表性的一段,可以发现两点:

    • 将之前的任务放在block中,从新以异步任务方式添加到队列中,就会在子线程中执行
    • "下载任务"会在"支付任务之前完成"
    2017-01-10 23:05:00.053 GCD-01[14704:1458015]  I come here    <NSThread: 0x60000006e440>{number = 1, name = main}
    2017-01-10 23:05:00.053 GCD-01[14704:1458400] 0 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
    2017-01-10 23:05:00.054 GCD-01[14704:1458400] 1 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
    2017-01-10 23:05:00.054 GCD-01[14704:1458400] 2 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
    2017-01-10 23:05:00.055 GCD-01[14704:1458400] 3 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
    2017-01-10 23:05:00.055 GCD-01[14704:1458400] 4 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
    2017-01-10 23:05:00.055 GCD-01[14704:1458400] 5 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
    2017-01-10 23:05:00.056 GCD-01[14704:1458400] 6 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
    2017-01-10 23:05:00.056 GCD-01[14704:1458400] 7 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
    2017-01-10 23:05:00.056 GCD-01[14704:1458400] 8 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
    2017-01-10 23:05:00.056 GCD-01[14704:1458400] 9 ------ <NSThread: 0x608000070600>{number = 6, name = (null)}
    2017-01-10 23:05:00.057 GCD-01[14704:1458400] 用户登录  <NSThread: 0x608000070600>{number = 6, name = (null)}
    2017-01-10 23:05:00.057 GCD-01[14704:1458400] 支付任务   <NSThread: 0x608000070600>{number = 6, name = (null)}
    2017-01-10 23:05:00.057 GCD-01[14704:1458089] 下载任务  <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
    
    
    
    
    2017-01-10 23:05:00.573 GCD-01[14704:1458015]  I come here    <NSThread: 0x60000006e440>{number = 1, name = main}
    2017-01-10 23:05:00.573 GCD-01[14704:1458089] 0 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
    2017-01-10 23:05:00.574 GCD-01[14704:1458089] 1 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
    2017-01-10 23:05:00.574 GCD-01[14704:1458089] 2 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
    2017-01-10 23:05:00.574 GCD-01[14704:1458089] 3 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
    2017-01-10 23:05:00.575 GCD-01[14704:1458089] 4 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
    2017-01-10 23:05:00.575 GCD-01[14704:1458089] 5 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
    2017-01-10 23:05:00.575 GCD-01[14704:1458089] 6 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
    2017-01-10 23:05:00.575 GCD-01[14704:1458089] 7 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
    2017-01-10 23:05:00.576 GCD-01[14704:1458089] 8 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
    2017-01-10 23:05:00.576 GCD-01[14704:1458089] 9 ------ <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
    2017-01-10 23:05:00.576 GCD-01[14704:1458089] 用户登录  <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
    2017-01-10 23:05:00.577 GCD-01[14704:1458400] 下载任务  <NSThread: 0x608000070600>{number = 6, name = (null)}
    2017-01-10 23:05:00.577 GCD-01[14704:1458089] 支付任务   <NSThread: 0x61800006dbc0>{number = 5, name = (null)}
    

    总结

    对于GCD的基本使用,注意以下三点:

    • 串行队列每次只能执行一个任务,不管是同步还是异步,因为队列本身只会等当前任务完成后才能去取出下一个任务,入口限制;
    • 并行队列同步执行时:即使能从队列中获取多个任务,也只能等待,因为同步执行,不会创建子线程,所有任务都只能在当前线程中完成,而当前线程,只能等当前任务执行完,才能去执行下一个任务,出口限制;
    • 并行队列异步执行:并行队列可以获取多个任务,异步执行可以获取多个线程,所以会出现并发执行,没有限制。

    相关文章

      网友评论

      • Noah1985:因为同步执行,不会创建子线程,所有任务都只能在当前线程中完成,而当前线程,只能等当前任务执行完,才能去执行下一个任务

        非要挑剔的话,这里其实有一点不是太严谨。
        即使所有任务都只在同一条线程上执行,但。。。下一个任务不一定非要等待上一个任务执行完成后才会执行。。。这个是单线程异步,类似node.js。
        当然,并不是说你这里错了,只是不严谨,结合上下文大家都知道想表达的意思。
        Noah1985:@EmptyWalker
        后台的话。。。。我暂时只知道node.js是这种模式。PHP之类的是没有event loop的,所以无法实现单线程异步操作。
        EmptyWalker:谢谢提示哈,学到了更多的知识,本文是为了简化简历队列类型和任务执行方式;所以没有深入探究。
        单线程异步:我自己也是看了你的回复才找了资料,看了看,之前都不了解这个概念,:flushed:
        不过这中方式,对于移动端来说,应该很少涉及,在后台的并发操作处理上,应用场景更多一些,可以减少性能开支:yum:

      本文标题:深入浅出GCD —— 所学即所用

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