GCD

1.简介

1.1 GCD官方解释

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

1.2 GCD的好处

  • GCD可用于多核的并行运算
  • GCD会自动利用更多的CPU内核(比如双核、四核)
  • GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
  • 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

2.任务和队列

学习GCD之前,我们要了解GCD中两个核心概念:任务和队列。

2.1 任务

任务:就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在GCD中是放在block中的。执行任务有两种方式:同步执行异步执行。两者的主要区别是是否具备开启新线程的能力。

  • 同步执行:只能在当前线程中执行任务,不具备开启新线程的能力。
  • 异步执行:可以在新的线程中执行任务,具备开启新线程的能力。

2.2 队列

队列:这里的队列指任务队列,即用来存放任务的队列。队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。在GCD中有两种队列:串行队列并行队列

  • 并行队列:可以让多个任务并行(同时)执行(自动开启多个线程同时执行任务),并行功能只有在异步函数下才有效。
  • 串行队列:让任务一个接着一个的执行(一个任务执行完毕后,再执行下一个任务)

3.GCD的使用步骤

GCD的使用步骤其实很简单,只有两步。

1.创建一个队列(串行队列或并行队列)

2.将任务添加到队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)

3.1 队列的创建方法

可以使用 dispatch_queue_create 来创建对象,需要传入两个参数,第一个参数表示队列的唯一标识符,用于Debug,可为空;第二个参数用来识别是串行队列还是并行队列。DISPATCH_QUEUE_SERIAL 表示串行队列,DISPATCH_QUEUE_CONCURRENT 表示并行队列。

// 串行队列的创建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// 并行队列的创建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

 在下图中,我们可以看到,唯一标识符的作用,在Debug时,更容易确定是哪个线程除了问题。

对于并行队列,还可以使用 dispatch_get_global_queue 来创建全局并行队列。GCD默认提供了全局的并行队列,需要传入两个参数。第一个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT,第二个参数暂时没用,用0即可。

3.2 任务的创建方法

// 同步执行任务创建方法
dispatch_sync(queue, ^{
    NSLog(@"%@",[NSThread currentThread]);    // 这里放任务代码
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
    NSLog(@"%@",[NSThread currentThread]);    // 这里放任务代码
});

 虽然使用GCD只需两步,但是既然我们有两种队列,两种任务执行方式,那么我们就有了四种不同的组合方式,这4种不同的组合方式是:

  1. 并行队列 + 同步执行
  2. 并行队列 + 异步执行
  3. 串行队列 + 同步执行
  4. 串行队列 + 异步执行

实际上,我们还有一种特殊队列是主队列,那样就有6种不同的组合方式:

  1. 主队列 + 同步执行
  2. 主队列 + 异步执行

那么这几种不同的组合方式有什么区别呢,看图:

  并行队列 串行队列 主队列
同步(sync) 没有开启新线程,串行执行任务 没有开启新线程,串行执行任务 没有开启新线程,串行执行任务
异步(async) 有开启新线程,并行执行任务 有开启新线程(1条),串行执行任务 没有开启新线程,串行执行任务

4. GCD的基本使用

4.1 并行队列 + 同步执行

不会开启新线程,执行完一个任务,再执行下一个任务

- (void)lesson2{
    // 创建一个并行队列
    NSLog(@"syncConcurrent---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("并行队列", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 1 - - - - %@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 2 - - - - %@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 3 - - - - %@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"syncConcurrent---end");
    
    /*
     我们可以看出,任务都是在主线程中执行的,由于只有一个线程,所以任务只能一个一个执行
     同时,我们还可以看到,都打印在 begin 和 end之间,说明,任务添加到队列之后,马上会执行的
     */
    
//    2017-12-06 18:30:42.338766+0800 GCD[20449:496505] syncConcurrent---begin
//    2017-12-06 18:30:42.339001+0800 GCD[20449:496505]  1 - - - - <NSThread: 0x600000065640>{number = 1, name = main}
//    2017-12-06 18:30:42.339187+0800 GCD[20449:496505]  1 - - - - <NSThread: 0x600000065640>{number = 1, name = main}
//    2017-12-06 18:30:42.339325+0800 GCD[20449:496505]  1 - - - - <NSThread: 0x600000065640>{number = 1, name = main}
//    2017-12-06 18:30:42.339448+0800 GCD[20449:496505]  2 - - - - <NSThread: 0x600000065640>{number = 1, name = main}
//    2017-12-06 18:30:42.339603+0800 GCD[20449:496505]  2 - - - - <NSThread: 0x600000065640>{number = 1, name = main}
//    2017-12-06 18:30:42.339763+0800 GCD[20449:496505]  2 - - - - <NSThread: 0x600000065640>{number = 1, name = main}
//    2017-12-06 18:30:42.339919+0800 GCD[20449:496505]  3 - - - - <NSThread: 0x600000065640>{number = 1, name = main}
//    2017-12-06 18:30:42.340269+0800 GCD[20449:496505]  3 - - - - <NSThread: 0x600000065640>{number = 1, name = main}
//    2017-12-06 18:30:42.340406+0800 GCD[20449:496505]  3 - - - - <NSThread: 0x600000065640>{number = 1, name = main}
//    2017-12-06 18:30:42.340537+0800 GCD[20449:496505] syncConcurrent---end
}

 4.2 并行队列 + 异步执行

可同时开启多线程,任务交替执行

- (void)lesson3{
    
    // 创建一个并行队列
    NSLog(@"syncConcurrent---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("并行队列", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 1 - - - - %@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 2 - - - - %@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 3 - - - - %@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"syncConcurrent---end");
  /*
   我们可以看出,除了主线程,又开启了3个线程,并且任务是交替同时执行的,另一方面可以看出,任   务不是马上执行,而是将所有任务添加到队列之后,才开始异步执行。
  */ // 2017-12-06 22:16:40.036449+0800 GCD[1001:46014] syncConcurrent---begin // 2017-12-06 22:16:40.036735+0800 GCD[1001:46014] syncConcurrent---end // 2017-12-06 22:16:40.036914+0800 GCD[1001:46328] 2 - - - - <NSThread: 0x6000004648c0>{number = 4, name = (null)} // 2017-12-06 22:16:40.036912+0800 GCD[1001:46325] 3 - - - - <NSThread: 0x60400027d380>{number = 5, name = (null)} // 2017-12-06 22:16:40.036956+0800 GCD[1001:46330] 1 - - - - <NSThread: 0x60400027d440>{number = 3, name = (null)} // 2017-12-06 22:16:40.037143+0800 GCD[1001:46328] 2 - - - - <NSThread: 0x6000004648c0>{number = 4, name = (null)} // 2017-12-06 22:16:40.037306+0800 GCD[1001:46330] 1 - - - - <NSThread: 0x60400027d440>{number = 3, name = (null)} // 2017-12-06 22:16:40.037337+0800 GCD[1001:46325] 3 - - - - <NSThread: 0x60400027d380>{number = 5, name = (null)} // 2017-12-06 22:16:40.037505+0800 GCD[1001:46328] 2 - - - - <NSThread: 0x6000004648c0>{number = 4, name = (null)} // 2017-12-06 22:16:40.037608+0800 GCD[1001:46330] 1 - - - - <NSThread: 0x60400027d440>{number = 3, name = (null)} // 2017-12-06 22:16:40.039677+0800 GCD[1001:46325] 3 - - - - <NSThread: 0x60400027d380>{number = 5, name = (null)} }

 4.3 串行队列 + 同步执行

不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行另一个任务

- (void)lesson4{
    NSLog(@"syncConcurrent - begin");
    
    dispatch_queue_t queue = dispatch_queue_create("串行队列", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 1 - - - - %@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 2 - - - - %@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 3 - - - - %@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"syncConcurrent - end");
  /* 我们可以看到,所有的任务都是在主线程中执行的,并没有开启新的线程,而且由于是串行队列,所以按顺序一个一个执行,而且任务都是添加到队列中马上执行的。
  */ // 2017-12-06 22:23:10.808603+0800 GCD[1066:51102] syncConcurrent - begin // 2017-12-06 22:23:10.809058+0800 GCD[1066:51102] 1 - - - - <NSThread: 0x604000079ec0>{number = 1, name = main} // 2017-12-06 22:23:10.809336+0800 GCD[1066:51102] 1 - - - - <NSThread: 0x604000079ec0>{number = 1, name = main} // 2017-12-06 22:23:10.809539+0800 GCD[1066:51102] 1 - - - - <NSThread: 0x604000079ec0>{number = 1, name = main} // 2017-12-06 22:23:10.809706+0800 GCD[1066:51102] 2 - - - - <NSThread: 0x604000079ec0>{number = 1, name = main} // 2017-12-06 22:23:10.809884+0800 GCD[1066:51102] 2 - - - - <NSThread: 0x604000079ec0>{number = 1, name = main} // 2017-12-06 22:23:10.810055+0800 GCD[1066:51102] 2 - - - - <NSThread: 0x604000079ec0>{number = 1, name = main} // 2017-12-06 22:23:10.810226+0800 GCD[1066:51102] 3 - - - - <NSThread: 0x604000079ec0>{number = 1, name = main} // 2017-12-06 22:23:10.810494+0800 GCD[1066:51102] 3 - - - - <NSThread: 0x604000079ec0>{number = 1, name = main} // 2017-12-06 22:23:10.810713+0800 GCD[1066:51102] 3 - - - - <NSThread: 0x604000079ec0>{number = 1, name = main} // 2017-12-06 22:23:10.810813+0800 GCD[1066:51102] syncConcurrent - end }

 4.4 串行队列 + 异步执行

会开启新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务。

- (void)lesson5{
    NSLog(@"syncConcurrent - begin");
    
    dispatch_queue_t queue = dispatch_queue_create("串行队列", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 1 - - - - %@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 2 - - - - %@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 3 - - - - %@",[NSThread currentThread]);
        }
    });
    
/* 我们可以看到,开启了一条新线程,但是任务还是串行,所以一个一个执行,任务不是马上执行,而是将所有任务添加到队列后才开始同步执行。*/ NSLog(@"syncConcurrent - end"); // 2017-12-06 22:25:04.040810+0800 GCD[1082:52532] syncConcurrent - begin // 2017-12-06 22:25:04.041050+0800 GCD[1082:52532] syncConcurrent - end // 2017-12-06 22:25:04.041171+0800 GCD[1082:52765] 1 - - - - <NSThread: 0x6040004607c0>{number = 3, name = (null)} // 2017-12-06 22:25:04.041814+0800 GCD[1082:52765] 1 - - - - <NSThread: 0x6040004607c0>{number = 3, name = (null)} // 2017-12-06 22:25:04.042084+0800 GCD[1082:52765] 1 - - - - <NSThread: 0x6040004607c0>{number = 3, name = (null)} // 2017-12-06 22:25:04.043690+0800 GCD[1082:52765] 2 - - - - <NSThread: 0x6040004607c0>{number = 3, name = (null)} // 2017-12-06 22:25:04.043849+0800 GCD[1082:52765] 2 - - - - <NSThread: 0x6040004607c0>{number = 3, name = (null)} // 2017-12-06 22:25:04.044082+0800 GCD[1082:52765] 2 - - - - <NSThread: 0x6040004607c0>{number = 3, name = (null)} // 2017-12-06 22:25:04.044733+0800 GCD[1082:52765] 3 - - - - <NSThread: 0x6040004607c0>{number = 3, name = (null)} // 2017-12-06 22:25:04.045220+0800 GCD[1082:52765] 3 - - - - <NSThread: 0x6040004607c0>{number = 3, name = (null)} // 2017-12-06 22:25:04.045922+0800 GCD[1082:52765] 3 - - - - <NSThread: 0x6040004607c0>{number = 3, name = (null)} }

主队列:GCD自带的一种特殊的串行队列,所有放在主队列中的任务,都会放到主线程中执行,可以通过dispatch_get_main_queue() 获得主队列。

 4.5 主队列 + 同步执行

互等卡住不可行(在主线程中调用)

- (void)lesson6{
    
    NSLog(@"主线程 + 同步执行 - begin");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 1 - - - - %@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 2 - - - - %@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 3 - - - - %@",[NSThread currentThread]);
        }
    });

  /*因为,我们在主线程中执行这段代码。我们把任务放到了主队列中,也就是放到了主线程的队列中。而同步执行有个特点,就是对于任务是立马执行的。那么当我们把第一个任务放进主队列中,它就会立马执行,但是主线程正在处理 lesson6 的方法,所以任务1需要等待 lesson6 执行完成才能执行,当lesson6执行到任务1的使用,又需要等任务1执行完成才能执行任务2和任务3,
那么,现在的情况就是 lesson6 方法和任务1都在等对方执行完毕。这样大家互相等待,所以就卡住了。*/

因为,此时主线程正在执行 方法lesson6, 但是由于是同步执行, 当将任务1添加到队列中时会马上执行,但是由于主线程正在执行方法lesson6,任务1需要等待lesson6执行完成,当lesson6执行到任务1的时候,也需要任务1执行完成才能继续执行,所以相互等待。

    
NSLog(@"主线程 + 同步执行 - end"); // 2017-12-06 22:31:04.600063+0800 GCD[1120:56056] 主线程 + 同步执行 - begin // (lldb) }

那么,如果不在主线程中调用,而在其它线程中调用会如何呢?

不会开启新线程,执行完一个任务,在执行下一个任务(在其它线程中调用)

- (void)lesson7{
    dispatch_queue_t queue = dispatch_queue_create("111", NULL);
    dispatch_async(queue, ^{
        [self lesson6];
    });
    /*所有任务都是在主线程中执行的,并没有开启新的线程,而且由于主队列是串行队列,所以按顺序一个一个执行,并且任务添加到队列中就会马上执行*/
//    2017-12-06 22:35:25.873497+0800 GCD[1140:58833] 主线程 + 同步执行 - begin
//    2017-12-06 22:35:25.878078+0800 GCD[1140:58563]  1 - - - - <NSThread: 0x600000066b80>{number = 1, name = main}
//    2017-12-06 22:35:25.878277+0800 GCD[1140:58563]  1 - - - - <NSThread: 0x600000066b80>{number = 1, name = main}
//    2017-12-06 22:35:25.878440+0800 GCD[1140:58563]  1 - - - - <NSThread: 0x600000066b80>{number = 1, name = main}
//    2017-12-06 22:35:25.879158+0800 GCD[1140:58563]  2 - - - - <NSThread: 0x600000066b80>{number = 1, name = main}
//    2017-12-06 22:35:25.879723+0800 GCD[1140:58563]  2 - - - - <NSThread: 0x600000066b80>{number = 1, name = main}
//    2017-12-06 22:35:25.880138+0800 GCD[1140:58563]  2 - - - - <NSThread: 0x600000066b80>{number = 1, name = main}
//    2017-12-06 22:35:25.880857+0800 GCD[1140:58563]  3 - - - - <NSThread: 0x600000066b80>{number = 1, name = main}
//    2017-12-06 22:35:25.881020+0800 GCD[1140:58563]  3 - - - - <NSThread: 0x600000066b80>{number = 1, name = main}
//    2017-12-06 22:35:25.881485+0800 GCD[1140:58563]  3 - - - - <NSThread: 0x600000066b80>{number = 1, name = main}
//    2017-12-06 22:35:25.881695+0800 GCD[1140:58833] 主线程 + 同步执行 - end
}

 4.6 主队列 + 异步执行

只有在主线程中执行任务,执行完一个任务,再执行下一个任务

- (void)lesson8{
    
    NSLog(@"主线程 + 异步执行 - begin");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 1 - - - - %@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 2 - - - - %@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            NSLog(@" 3 - - - - %@",[NSThread currentThread]);
        }
    });
    /*我们发现所有任务都是在主线程中,虽然是异步执行,具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中,并一个一个执行,并且任务不是马上执行,而是将所有任务添加到队列之后,才开始同步执行。
  */ NSLog(@"主线程 + 异步执行 - end"); // 2017-12-06 22:40:01.452127+0800 GCD[1176:61800] 主线程 + 异步执行 - begin // 2017-12-06 22:40:01.452394+0800 GCD[1176:61800] 主线程 + 异步执行 - end // 2017-12-06 22:40:01.457218+0800 GCD[1176:61800] 1 - - - - <NSThread: 0x60000006d940>{number = 1, name = main} // 2017-12-06 22:40:01.457404+0800 GCD[1176:61800] 1 - - - - <NSThread: 0x60000006d940>{number = 1, name = main} // 2017-12-06 22:40:01.457966+0800 GCD[1176:61800] 1 - - - - <NSThread: 0x60000006d940>{number = 1, name = main} // 2017-12-06 22:40:01.458234+0800 GCD[1176:61800] 2 - - - - <NSThread: 0x60000006d940>{number = 1, name = main} // 2017-12-06 22:40:01.458409+0800 GCD[1176:61800] 2 - - - - <NSThread: 0x60000006d940>{number = 1, name = main} // 2017-12-06 22:40:01.458602+0800 GCD[1176:61800] 2 - - - - <NSThread: 0x60000006d940>{number = 1, name = main} // 2017-12-06 22:40:01.458829+0800 GCD[1176:61800] 3 - - - - <NSThread: 0x60000006d940>{number = 1, name = main} // 2017-12-06 22:40:01.458990+0800 GCD[1176:61800] 3 - - - - <NSThread: 0x60000006d940>{number = 1, name = main} // 2017-12-06 22:40:01.459240+0800 GCD[1176:61800] 3 - - - - <NSThread: 0x60000006d940>{number = 1, name = main} }

 5.GCD线程之间的通讯

 在iOS开发过程中,我们一般在主线程里边进行UI刷新,例如:点击、滚动、拖拽等事件。我们通常把一些耗时的操作放在其他线程,比如说图片下载、文件上传等耗时操作。而当我们有时候在其它线程完成了耗时操作时,需要回到主线程,那么就用到了线程之间的通讯。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    for (int i = 0; i < 2; ++i) {
        NSLog(@"1------%@",[NSThread currentThread]);
    }

    // 回到主线程
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"2-------%@",[NSThread currentThread]);
    });
});

 

版权声明:本文为chenjiangxiaoyu原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:http://www.cnblogs.com/chenjiangxiaoyu/p/7994173.html