Grand Central Dispatch (GCD) 是Apple开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务,在ios4以后使用
使用GCD的好处
- GCD可用于多核的并行运算(这点可以更好的发挥多核的优势)
- GCD会自动的利用调度更多的CPU内核
- GCD是自动管理线程的生命周期(创建,任务调度 ,线程销毁)
- 只需编写业务逻辑不需要编写线程管理代码
2.任务与队列
- **任务:**任务就是放在线程中要执行的内容,在GCD Block中需要执行的内容,
- 执行任务分为同步执行和异步执行区别在于是否具备开启线程的能力
- **同步执行(sync):**只能在当前线程中执行任务,不具备开启线程的能力
- **异步执行(async):**可以在新的线程中执行任务,具备开启新线程的能力
队列: 这里的队列指任务队列,即用来存放任务的队列。队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。在GCD中有两种队列:串行队列和并行队列
- 并行队列 (Concurrent): 可以让多个任务并行(同时)执行
- 并行功能只在异步(dispatch_async)函数下有效
- 串行队列(Serial) 任务顺序执行的队列
3.GCD的使用 GCD使用分为两步
-
创建一个队列(串行队列和并行队列)
-
将任务添加到队列中去
1.队列的创建
- 使用dispatch_queue_create 创建对象,传入两个参数,第一个参数是队列的唯一标志符,可为空,第二个参数用来标志队列的类型**(串行队列Serial 并行队列Concurrent)**
// 串行队列的创建方法
dispatch_queue_t queue= dispatch_queue_create("queueSerial", DISPATCH_QUEUE_SERIAL);
// 并行队列的创建方法
dispatch_queue_t queue= dispatch_queue_create("queueConcurrent", DISPATCH_QUEUE_CONCURRENT);
- 并行队列可以使用dispatch_get_global_queue来创建全局的并行队列,默认提供的是全局的并行队列 ,需要传入两个参数,一般是默认参数0,0 或者 0,DISPATCH_QUEUE_PRIORITY_DEFAULT
2.任务的创建
// 同步执行任务创建方法
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]); // 这里放任务代码
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]); // 这里放任务代码
});
上面是简单的对队列和任务的创建,在实际使用中我们大多使用的组合形式的使用
- 并行同步执行
- 并行异步执行
- 串行同步执行
- 串行异步执行
下面来看一下具体的执行
1.并行同步执行
dispatch_queue_t concurrentQueue = dispatch_queue_create("ConCurrent", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"=======开始执行");
//同步执行
dispatch_sync(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"1----%@",[NSThread currentThread]);
}
dispatch_sync(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"2----%@",[NSThread currentThread]);
}
});
});
NSLog(@"=======执行结束");
看一下输出结果 从图片中可以看出,任务是顺序执行的,
2.并行异步执行
dispatch_queue_t concurrentQueue = dispatch_queue_create("ConCurrent", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"=======开始执行");
//异步执行
dispatch_async(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_async(concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"2----%@",[NSThread currentThread]);
}
});
NSLog(@"=======执行结束");
执行结果看出任务是先被先加入线程队列中后才开始执行的,并且顺序交替执行任务 3.串行同步执行
- 不会开启新的线程,在当前线程执行任务,任务是串行执行,执行完一个任务,在执行下一个任务
//串行同步执行
dispatch_queue_t SerialQueue = dispatch_queue_create("SerialCurrent", DISPATCH_QUEUE_SERIAL);
NSLog(@"=======开始执行");
dispatch_sync(SerialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_sync(SerialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"2----%@",[NSThread currentThread]);
}
});
NSLog(@"=======执行结束");
任务是顺序执行,介于开始于结束之间,说明任务被添加到队列后立即执行,感觉和并行同步执行一样
4.串行异步执行
- 会开启线程新线程,但因为是串行队列,所以会顺讯执行,一个任务结束才会执行下一个任务
dispatch_queue_t SerialQueue = dispatch_queue_create("SerialCurrent", DISPATCH_QUEUE_SERIAL);
NSLog(@"=======开始执行");
dispatch_async(SerialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_async(SerialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"2----%@",[NSThread currentThread]);
}
});
NSLog(@"=======执行结束");
######从打印结果来看,任务是先都添加到队列中才执行的,由于是串行队列所以程序是顺序执行的,
GCD中一个重要的函数使用(dispatch_group_notify) 在实际开发中,多任务的执行,我们最终想获得任务都已执行完毕的通知就需要使用到(dispatch_group_notify) 具体的使用法
//这里创建的是一个全局的并行
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 (int i = 0; i < 3; i++) {
NSLog(@"任务一");
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任务二");
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任务三");
}
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"任务执行完成");
});
######执行结果可以看出任务dispatch_get_global_queue是一个并行队列(全局的并行队列) notify在其他任务都执行完成后才会执行
4.GCD中另一个很重要的函数dispatch_barrier_async
从字面上理解就是一个栅栏,就好像是一道隔离墙一样,将上下分离隔开,开发中应用场景为,上面添加到队列的线程任务执行完成在执行其他添加到队列中的线程任务
dispatch_queue_t barriQueue = dispatch_queue_create("barri", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"=======开始执行");
dispatch_async(barriQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_async(barriQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"2----%@",[NSThread currentThread]);
}
});
dispatch_barrier_async(barriQueue, ^{
NSLog(@"barri =====%@",[NSThread currentThread]);
});
dispatch_async(barriQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"4----%@",[NSThread currentThread]);
}
});
dispatch_async(barriQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"5----%@",[NSThread currentThread]);
}
});
NSLog(@"=======执行结束");
}
2017-11-02 11:38:37.035 TestApp[6549:670187] =======开始执行
2017-11-02 11:38:37.036 TestApp[6549:670187] =======执行结束
2017-11-02 11:38:37.036 TestApp[6549:670603] 2----<NSThread: 0x60000006aa80>{number = 4, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670582] 1----<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670603] 2----<NSThread: 0x60000006aa80>{number = 4, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670582] 1----<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670603] 2----<NSThread: 0x60000006aa80>{number = 4, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670582] 1----<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670582] barri =====<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670582] 4----<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670603] 5----<NSThread: 0x60000006aa80>{number = 4, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670582] 4----<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.036 TestApp[6549:670603] 5----<NSThread: 0x60000006aa80>{number = 4, name = (null)}
2017-11-02 11:38:37.037 TestApp[6549:670582] 4----<NSThread: 0x61800006f2c0>{number = 3, name = (null)}
2017-11-02 11:38:37.037 TestApp[6549:670603] 5----<NSThread: 0x60000006aa80>{number = 4, name = (null)}
执行结果看,虽然我们创建的是并行异步执行,但是barri以上的执行完成以后才会执行到barri下面的代码
再添加一个很重要的概念,GCD的信号量(dispatch_semaphore_t)---也是线程安全的,当信号不为0是程序等待
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t sema = dispatch_semaphore_create(1); //创建信号量 传入的(1)为初始号量
NSMutableArray *array = [NSMutableArray arrayWithCapacity:1];
for (int i = 0; i < 10; i++) {
@autoreleasepool {
dispatch_async(queue, ^{ //异步线程,不阻塞其他运行
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);//等待信号(信号量减一) ,在这之前不为0才会继续执行,为0就不会往下执行
NSLog(@"信号减");
[array addObject:[NSNumber numberWithInt:i]];
dispatch_semaphore_signal(sema); //信号量加一
NSLog(@"信号加");
});
}
}