GCD
简介
Grand Central Dispatch(GCD)是 Apple 开发的一个多核编程的解决方法。它是一套纯 C 语言的 API
,主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。
GCD的优点
- GCD支持多核并行计算
- GCD自动管理线程的生命周期(线程的创建、调起、等待、销毁)
- 使用者只需告知GCD执行任务
任务与队列
任务
任务即执行操作
,就是线程中需要执行的代码。在GCD
中就是放在block
中执行的代码都是任务。任务分为同步任务(sync)
和异步任务(async)
。
同步任务(sync)
:同步添加到队列当中,当前面的任务没有执行完毕时会一直等待,直到前面的任务执行完毕才会执行当前加入的任务。不可以在新的线程中执行,不具备开辟新的线程能力。异步任务(async)
:异步添加到队列当中,无需等待前面的任务执行完成才执行,加入队列就会执行。可以在新的线程中执行,具备开辟新的线程能力。注意:具备开启新线程的能力不代表一定开启新的线程
队列
指执行任务的队列,就是等待执行的任务列表,遵循先进先出IFFO
原则。在GCD中有串行队列(Serial Dispatch Queue)
和并行队列(Concurrent Dispatch Queue),又叫并发队列
。
串行队列(Serial Dispatch Queue)
:只开启一个线程
,每次只有一个任务执行,任务执行完成后才会执行下一个任务。并行队列(Concurrent Dispatch Queue)
:可以让多个任务同时执行,可以开启多个线程来执行多个任务。注意:并发队列只有在异步函数下才有效
。
特殊队列
- 串行主队列:一种串行的主队列,在主队列中调用不能在主线程中再次调用同步任务,否则会造成堵塞,但是可以在其他线程调用同步任务
- 全局并发队列:一种全局的并发队列
队列与任务的创建
创建队列
- 创建串行队列
//第一个参数是队列的名字,自定义;第二个参数表示是串行还是并发
//DISPATCH_QUEUE_SERIAL = NULL,因此DISPATCH_QUEUE_SERIAL可以用NULL代替创建串行队列
dispatch_queue_t queue = dispatch_queue_create("shifx", DISPATCH_QUEUE_SERIAL);
- 创建并发队列
//第一个参数是队列的名字,自定义;第二个参数表示是串行还是并发
dispatch_queue_t queue = dispatch_queue_create("shifx", DISPATCH_QUEUE_CONCURRENT);
任务的创建
- 创建同步任务
//创建同步任务(queue是队列)
dispatch_sync(queue, ^{
});
- 创建异步任务
//创建异步任务(queue是队列)
dispatch_async(queue, ^{
});
队列与任务总结
综上所述,我们可以得出一共四种组合外加两种特殊的队列,即
- 串行队列+同步执行
- 并发队列+同步执行
- 串行队列+异步执行
- 并发队列+异步执行
- 串行主队列
- 全局并发队列
队列与任务组合使用
串行队列+同步执行
代码实现
-(void)serialSysncText {
//串行队列
dispatch_queue_t serial = dispatch_queue_create("shifx", DISPATCH_QUEUE_SERIAL);
NSLog(@"串行同步任务开始");
dispatch_sync(serial, ^{
sleep(1);
for (int i = 0; i<3; i++) {
NSLog(@"串行同步任务1 = %d",i);
}
});
dispatch_sync(serial, ^{
sleep(1);
for (int i = 0; i<3; i++) {
NSLog(@"串行同步任务2 = %d",i);
}
});
NSLog(@"串行同步任务结束");
}
运行结果
从运行结果当中,我们可以看到串行队列中的同步任务都是按照先后顺序依次执行的。
并发队列+同步任务
代码实现
-(void)concurrentAsysncText {
//并发队列
dispatch_queue_t concurrent = dispatch_queue_create("shifx", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"并发同步任务开始");
dispatch_sync(concurrent, ^{
sleep(1);
for (int i = 0; i<3; i++) {
NSLog(@"并发同步任务1 = %d",i);
}
});
dispatch_sync(concurrent, ^{
sleep(1);
for (int i = 0; i<3; i++) {
NSLog(@"并发同步任务2 = %d",i);
}
});
NSLog(@"并发同步任务结束");
}
运行结果
从结果得知,并发队列执行的同步任务也是按照顺序依次执行
的,因为它们都在主线程中执行
的,没有开启新的线程。感兴趣的小伙伴可以打印下线程看下。
串行队列+异步执行
代码实现
-(void)serialAsysncText {
//串行队列
dispatch_queue_t serial = dispatch_queue_create("shifx", DISPATCH_QUEUE_SERIAL);
NSLog(@"串行异步任务开始");
dispatch_async(serial, ^{
for (int i = 0; i<3; i++) {
sleep(1);
NSLog(@"串行异步任务1 = %d",i);
NSLog(@"当前线程:%@",[NSThread currentThread]);
}
});
dispatch_async(serial, ^{
for (int i = 0; i<3; i++) {
sleep(1);
NSLog(@"串行异步任务2 = %d",i);
NSLog(@"当前线程:%@",[NSThread currentThread]);
}
});
NSLog(@"串行异步任务结束");
}
运行结果
从运行结果可以看出,
- 所有的异步任务都是在打印串行异步任务结束后开始执行;
- 开启了新的线程,但只开启了一个线程;
- 在串行队列中的异步任务也是按照先后顺序执行。
并发队列+异步执行
代码实现
-(void)concurrentAsysncText {
//并发队列
dispatch_queue_t concurrent = dispatch_queue_create("shifx", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"并发异步任务开始");
dispatch_async(concurrent, ^{
for (int i = 0; i<3; i++) {
sleep(1);
NSLog(@"并发异步任务1 = %d",i);
NSLog(@"当前线程:%@",[NSThread currentThread]);
}
});
dispatch_async(concurrent, ^{
for (int i = 0; i<3; i++) {
sleep(1);
NSLog(@"并发异步任务2 = %d",i);
NSLog(@"当前线程:%@",[NSThread currentThread]);
}
});
NSLog(@"并发异步任务结束");
}
运行结果
从运行结果来看,
- 所有的异步任务都是在打印并发异步任务结束后开始执行;
- 由于两个异步任务,并发队列开启了两个新的线程
- 两个异步任务执行没有先后顺序
主队列 + 同步任务
在主线程调用主队列同步任务
由于主队列是串行队列,它不能在主线程中调用主队列同步任务,如下所示崩溃原因就是同步任务放在了主线程中,需要等待主线程执行完毕才可以,而主线程需要等待该方法执行完成,两者互相等待造成死锁
,所以崩溃了。
其他线程调用主队列同步任务
代码
//使用 NSThread 的 detachNewThreadWithBlock 方法会创建线程
[NSThread detachNewThreadWithBlock:^{ [self mainSysncText];
}];
-(void)mainSysncText {
NSLog(@"当前所在的线程:%@",[NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{
for (int i = 0; i<2; i++) {
sleep(1);
NSLog(@"主队列同步任务1 = %d",i);
NSLog(@"当前线程:%@",[NSThread currentThread]);
}
});
dispatch_sync(dispatch_get_main_queue(), ^{
for (int i = 0; i<2; i++) {
sleep(1);
NSLog(@"主队列同步任务2 = %d",i);
NSLog(@"当前线程:%@",[NSThread currentThread]);
}
});
}
执行结果
主队列+异步任务
由于主队列是串行队列,所以主队列+异步任务
就相当于串行队列+异步任务
代码
-(void)mainAsysncText {
NSLog(@"当前所在的线程:%@",[NSThread currentThread]);
NSLog(@"主队列异步任务开始");
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i<2; i++) {
sleep(1);
NSLog(@"主队列异步任务1 = %d",i);
NSLog(@"当前线程:%@",[NSThread currentThread]);
}
});
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i<2; i++) {
sleep(1);
NSLog(@"主队列异步任务2 = %d",i);
NSLog(@"当前线程:%@",[NSThread currentThread]);
}
});
NSLog(@"主队列异步任务结束");
}
运行结果
效果等同于串行队列+异步任务