多线程

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]);
    });
});

6.GCD其它的方法 

6.1 dispatch_after

  如果想在指定时间后执行处理的情况,可以使用 dispatch_after 方法来实现。

  在3秒后将指定的Block追加到 Main Dispatch Queue 中的源代码如下:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"等待至少三秒钟");
    });

   这里需要注意的是,dispatch_after 并不是在指定时间后执行处理,而只是在指定时间追加处理到 Dispatch Queue,此源代码与在3秒后用dispatch_async函数追加 Block 到 Main Dispatch Queue 的相同。

  因为 Main Dispatch Queue 在主线程的 RunLoop 中执行,所以在比如每隔 1/60 秒执行的 RunLoop 中,Block 最快在 3 秒后执行,最慢在 3 秒 + 1/60 秒后执行,并且在 Main Dispatch Queue 有大量处理追加或主线程的处理本身有延迟时,这个时间会延长。

  虽然在有严格时间的要求下使用时会出现问题,但是想在大致延迟执行处理时,这个方法很好用。  

  第一个参数经常使用的值是 DISPATCH_TIME_NOW ,这表示现在的时间。

  第二个参数是指定延后的时间

  第三个参数是指定要追加处理的 Dispatch Queue

  第四个参数是要执行处理的Block 

6.2 dispatch Group

  在追加到 dispatch queue 中的多个处理全部结束后想执行结束处理,这种情况经常出现,只使用一个 串行队列时,只要将想执行的处理全部加到该串行队列,并在最后追加结束处理,就可以实现,但是在使用 并行队列,或同时使用多个 dispatch queue 时,就需要用到 dispatch group。

  例如,追加3个Block 到 Global Dispatch Queue,这些 Block 如果全部执行完毕,就会执行 Main Dispatch Queue 中结束处理用的 Block。

- (void)viewDidLoad {
    [super viewDidLoad];
    
    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, ^{
        for (NSInteger i = 0; i < 3; i++) {
            sleep(1);
            NSLog(@"async1 ------ %ld ----- %@",i,[NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            sleep(1);
            NSLog(@"async2 ------ %ld ----- %@",i,[NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, queue, ^{
        for (NSInteger i = 0; i < 3; i++) {
            sleep(1);
            NSLog(@"async3 ------ %ld ----- %@",i,[NSThread currentThread]);
        }
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"终于到我了 --- %@",[NSThread currentThread]);
    });
    
}

 输出结果:

2017-12-13 18:04:07.948776+0800 GCD[16160:439266] async1 ------ 0 ----- <NSThread: 0x600000278a00>{number = 3, name = (null)}
2017-12-13 18:04:07.948781+0800 GCD[16160:439267] async3 ------ 0 ----- <NSThread: 0x60400046dac0>{number = 5, name = (null)}
2017-12-13 18:04:07.948816+0800 GCD[16160:439268] async2 ------ 0 ----- <NSThread: 0x60400046d9c0>{number = 4, name = (null)}
2017-12-13 18:04:08.949200+0800 GCD[16160:439267] async3 ------ 1 ----- <NSThread: 0x60400046dac0>{number = 5, name = (null)}
2017-12-13 18:04:08.949217+0800 GCD[16160:439266] async1 ------ 1 ----- <NSThread: 0x600000278a00>{number = 3, name = (null)}
2017-12-13 18:04:08.949224+0800 GCD[16160:439268] async2 ------ 1 ----- <NSThread: 0x60400046d9c0>{number = 4, name = (null)}
2017-12-13 18:04:09.949591+0800 GCD[16160:439267] async3 ------ 2 ----- <NSThread: 0x60400046dac0>{number = 5, name = (null)}
2017-12-13 18:04:09.949769+0800 GCD[16160:439266] async1 ------ 2 ----- <NSThread: 0x600000278a00>{number = 3, name = (null)}
2017-12-13 18:04:09.955017+0800 GCD[16160:439268] async2 ------ 2 ----- <NSThread: 0x60400046d9c0>{number = 4, name = (null)}
2017-12-13 18:04:09.955370+0800 GCD[16160:438995] 终于到我了 --- <NSThread: 0x6000002613c0>{number = 1, name = main}

   我们可以看到,当三个追加Block的任务执行完毕后,主线程的任务才执行,无论向什么样的 dispatch queue 中追加处理,使用 dispatch group 都可以监视这些处理执行的结束。一旦检测到所有处理执行结束,就可将结束的处理追加到 dispatch queue 中,这就是使用 dispatch group 的原因。

6.3 dispatch_semaphore (信号量)

  在这里我们可以先考虑一个问题:假设现在系统有两个空闲资源可以使用,但同一时间要有三个线程需要访问,这种情况下,该怎么处理呢?或者,我们要下载很多图片,并发异步执行,但是我们又担心太多线程cpu吃不消,那么我们就可以使用信号量来控制一下线程最大开辟数。

  信号量:就是一种可用来控制访问资源的数量的标识。设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统可以按照我们们指定的信号量数量来执行多个线程。有点类似于锁的机制。

  主要有三个函数:

//创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量值)
 
//等待信号量(减少信号量)
dispatch_semaphore_wait(信号量,等待时间)
 
//发送信号量 (增加信号量)
dispatch_semaphore_signal(信号量)       一般都是先 降低,然后再提高

   一切都在代码中:

  

// creat 的 value 表示,最多有几个资源可以访问
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    // 获得全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 任务1
    dispatch_async(queue, ^{
        // 如果没有就无限制的等待
        long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"任务1 result - %ld",result);
        NSLog(@"开始执行任务1");
        sleep(4);
        NSLog(@"结束执行任务1");
        dispatch_semaphore_signal(semaphore);
    });
    
    // 任务2
    dispatch_async(queue, ^{
        // 如果没有就无限制的等待
        long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"任务2 result - %ld",result);
        NSLog(@"开始执行任务2");
        sleep(4);
        NSLog(@"结束执行任务2");
        dispatch_semaphore_signal(semaphore);
    });
    
    // 任务3
    dispatch_async(queue, ^{
        // 如果没有就无限制的等待
        long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"任务3 result - %ld",result);
        NSLog(@"开始执行任务3");
        sleep(3);
        NSLog(@"结束执行任务3");
        dispatch_semaphore_signal(semaphore);
    });

   执行完的代码:

2018-02-01 14:33:45.866447+0800 定时器[1465:121647] 任务2 result - 0
2018-02-01 14:33:45.866480+0800 定时器[1465:121648] 任务1 result - 0
2018-02-01 14:33:45.867231+0800 定时器[1465:121647] 开始执行任务2
2018-02-01 14:33:45.867241+0800 定时器[1465:121648] 开始执行任务1
2018-02-01 14:33:47.869069+0800 定时器[1465:121648] 结束执行任务1
2018-02-01 14:33:47.869292+0800 定时器[1465:121645] 任务3 result - 0
2018-02-01 14:33:47.869420+0800 定时器[1465:121645] 开始执行任务3
2018-02-01 14:33:49.869980+0800 定时器[1465:121647] 结束执行任务2
2018-02-01 14:33:50.870619+0800 定时器[1465:121645] 结束执行任务3

   我们可以看出来,因为只有2个资源,所以当任务1任务2占了两个资源之后,任务3就只能一直等待,直到任务1和任务2之间有一个任务完成之后,才能往下执行,我们设置的是无限制等待,如果我们设置任务3的等待时间为1的话,且任务1和任务2的执行时间超过1,那么 wait 方法就会返回一个不为 0 的长整型。这个例子只是简单的理解一下信号量的作用。

 6.4 dispatch_barrier 

  简单的先来理解一下,在一个并行队列中,有多个线程在执行多个任务,在这个并行队列中,有一个dispatch_barrier任务,这样会有一个什么效果呢?我们直接上代码:

// 创建一个并行队列
    dispatch_queue_t queue = dispatch_queue_create(0,DISPATCH_QUEUE_CONCURRENT);
    
    // 任务1
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 10; i++) {
            sleep(1);
            NSLog(@"任务1中的 - %ld",i);
        }
        NSLog(@"----------------------------");
    });
    
    // 任务2
    dispatch_barrier_async(queue, ^{
        for (NSInteger i = 10; i < 20; i++) {
            sleep(1);
            NSLog(@"任务2中的 - %ld",i);
        }
        NSLog(@"任务2当前线程 - %@",[NSThread currentThread]);
        NSLog(@"----------------------------");
    });
    
    NSLog(@"我是主线程");
    
    // 任务3
    dispatch_async(queue, ^{
        for (NSInteger i = 20; i < 30; i++) {
            sleep(1);
            NSLog(@"任务3中的 - %ld",i);
        }
    });

   我们可以看到,任务1先执行完,然后执行 barrier 里面的,再执行 任务3。dispatch_barrier_sync确实是会在队列中充当一个栅栏的作用,凡是在他之后进入队列的任务,总会在dispatch_barrier_sync之前的所有任务执行完毕之后才执行。见名知意,dispatch_barrier_sync是会在主线程执行队列中的任务的,所以,Running on Main Thread这句话会被阻塞,从而在barrier之后执行。也就是说 dispatch_barrier_sync 和 dispatch_barrier_async 的区别就在于,是否会阻塞主线程。

  注意:这个函数仅对于自己创建的并行队列有效。如果在全局队列中,因为每一次使用,系统都可能给你分配不同的并行队列,所以也就变得没有意义,串行队列也就不用说了。

6.5 GCD 精准的定时器

dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
// 第二个参数是间隔时间,第四个参数是精确度,0表示没有误差
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"123123123");
    });
    
    dispatch_resume(timer);
原文地址:https://www.cnblogs.com/chenjiangxiaoyu/p/7994173.html