1、什么是GCD?
GCD全称是Grand Central Dispatch,可译为“强大的调度器”。iOS 4开始引入的新一代的多线程编程技术。 开发者只需定义想执行的任务并追加到适当的Dispatch Queu中,GCD就能生成必要的线程并有计划的执行任务。
2、什么是任务?
想要做什么事情,在GCD中是放在block中的。
3、什么是串行队列 和 并行队列 ?
串行队列 (DISPATCH_QUEUE_SERIAL): 队列里的任务会以串行的形式一个一个按顺序执行
并行队列(DISPATCH_QUEUE_CONCURRENT) : 并发队列允许多个线程同时运行,并发功能只有在异步(dispatch_async)函数下才有效
4、什么是同步(sync) 和 异步(async)
同步 : 不具备开启新线程的能力,任务创建后就要执行完才能往下走
异步 : 具备开启新线程的能力,任务创建后可以先绕过回头再执行
5、特殊队列 ?
主队列 : GCD自带的一种特殊的串行队列 所有放在主队列中的任务,都会放到主线程中执行
6、如何创建队列 ?
1)创建一个串行队列(DISPATCH_QUEUE_SERUAL)
2) 创建一个并行队列(DISPATCH_QUEUE_CONCURRET)
3) 获取主队列(串行队列)
1)串行队列 + 同步执行
// 1、创建一个串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("队列唯一标识符", DISPATCH_QUEUE_SERIAL);
NSLog(@"--start--");
// 2、 将任务添加到队列中去
dispatch_sync(serialQueue, ^{
NSLog(@"任务1--%@", [NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"任务2--%@", [NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"任务3--%@", [NSThread currentThread]);
});
NSLog(@"--end--");
}
解释 : 同步执行意味着,不能开启新的线程,任务创建后必须执行完才能往下走.串行队列意味着任务必须按照顺序执行。两者结合后的后果就是 : 在主线程中按照顺序执行。
.
.
.
.
2) 串行队列 + 异步执行
// 1、创建一个串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("队列唯一标识符", DISPATCH_QUEUE_SERIAL);
NSLog(@"--start--");
// 2、 将任务添加到队列中去
dispatch_async(serialQueue, ^{
NSLog(@"任务1---%@",[NSThread currentThread]);
});
dispatch_async(serialQueue, ^{
NSLog(@"任务2---%@",[NSThread currentThread]);
});
dispatch_async(serialQueue, ^{
NSLog(@"任务3---%@",[NSThread currentThread]);
});
NSLog(@"--end--");
}
解释 : 异步执行意味可以开启新线程,任务可以先绕过不执行,回过头来再执行。串行队列必须按照顺序执行。两者结合的结果就是 : 开启一个新的线程,函数在执行时,先打印 NSLog(@”–start–”); 再按照顺序执行队列中的任务。
.
.
.
3) 并行队列 + 同步执行
// 创建并行队列
dispatch_queue_t currentQuque = dispatch_queue_create("并行队列",DISPATCH_QUEUE_CONCURRENT);
NSLog(@"--start--");
dispatch_sync(currentQuque, ^{
NSLog(@"任务1--%@",[NSThread currentThread]);
});
dispatch_sync(currentQuque, ^{
NSLog(@"任务2---%@", [NSThread currentThread]);
});
dispatch_sync(currentQuque, ^{
NSLog(@"任务1---%@", [NSThread currentThread]);
});
NSLog(@"--end--");
}
解释 : 同步执行执行意味着,不能开启新的线程,任务创建后必须执行完才能往下走,并行队列意味着,任务必须按添加进队列的顺序挨个执行,两者结合后 : 所有任务都只能在主线程中执行,函数在执行时,必须按照代码的书写顺序一行一行地执行完才能继续.在这里即便是并行队列,任务可以同时执行,但是由于只存在一个主线程,所以没法把任务分发到不同的线程去同步处理,其结果就是只能在主线程里按顺序挨个挨个执行了.
.
.
.
.
4) 并行队列 + 异步执行
//创建队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("并发队列", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"--start---");
dispatch_async(concurrentQueue, ^{
NSLog(@"任务1---%@", [NSThread currentThread]);
});
dispatch_async(concurrentQueue, ^{
NSLog(@"任务2--%@", [NSThread currentThread]);
});
dispatch_async(concurrentQueue, ^{
NSLog(@"任务3--%@", [NSThread currentThread]);
});
NSLog(@"--end---");
解释 : 异步执行意味着,可以开启新的线程,任务可以先绕过不执行,回头再来执行,并行队列意味着,任务之间不需要排队,且具有同时被执行的“权利”,两者结合: 开了三个新线程,这三个任务是同时执行的,没有先后
.
.
.
.
5) 主队列 (特殊的串行队列)+ 同步执行 线程死锁
- (void)syncMainQueue
{
// 获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
NSLog(@"--start--");
// 2、 将任务添加到队列中去
dispatch_sync(mainQueue, ^{
NSLog(@"任务1");
});
NSLog(@"--end--");
}
解释 : 造成了线程死锁。主队列中的任务必须按照顺序执行,任务1到等主线程有空才能执行,主线程要等”end” 结束后才有空。任务1和“end”相互等待。
.
.
.
.
6) 主队列 + 异步执行
// 获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
NSLog(@"--start--");
// 2、 将任务添加到队列中去
dispatch_async(mainQueue, ^{
NSLog(@"任务1--%@", [NSThread currentThread]);
});
dispatch_async(mainQueue, ^{
NSLog(@"任务2--%@", [NSThread currentThread]);
});
dispatch_async(mainQueue, ^{
NSLog(@"任务3--%@", [NSThread currentThread]);
});
NSLog(@"--end--");
解释 :主队列中的任务必须在主线程中执行,不允许在子线程中执行 ,所有任务都可以先跳过,之后再来“按顺序”执行
.
.
.
.
8、全局队列
GCD 帮你生成的并行队列,GCD默认已经提供了全局的并发队列供整个应用使用,所以可以不用手动创建。
//全局队列(并发队列) 参数说明
dispatch_get_global_queue(<#long identifier#>, <#unsigned
long flags#>)
long identifier ios 8.0 告诉队列执行任务的“服务质量 quality of service”,系统提供的参数有:
QOS_CLASS_USER_INTERACTIVE, 用户交互(希望尽快完成,用户对结果很期望,不要放太耗时操作)
QOS_CLASS_USER_INITIATED , 用户期望(不要放太耗时操作)
QOS_CLASS_DEFAULT 0x15, 默认(不是给程序员使用的,用来重置对列使用的)
QOS_CLASS_UTILITY 0x11, 实用工具(耗时操作,可以使用这个选项)
QOS_CLASS_BACKGROUND 0x09, 后台
QOS_CLASS_UNSPECIFIED 0x00, 未指定
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; ++i) {
dispatch_async(globalQueue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
解释: 异步意味着可以创新新的线程,并发队列+异步 可参数并发同时进行。
9、线程间的通信
在iOS开发过程中,我们一般在主线程里边进行UI刷新,例如:点击、滚动、拖拽等事件。我们通常把一些耗时的操作放在其他线程,比如说图片下载、文件上传等耗时操作。而当我们有时候在其他线程完成了耗时操作时,需要回到主线程,那么就用到了线程之间的通讯。
- (void)globalQueueAndMainQueue
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
// 回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"2-------%@",[NSThread currentThread]);
});
});
// 可以看到在其他线程中先执行操作,执行完了之后回到主线程执行主线程的相应操作。
}//线程之间的通信
.
.
.
10、GCD 的珊栏方法
我们有时需要异步执行两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于栅栏一样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到dispatch_barrier_async方法在两个操作组间形成栅栏
- (void)gcdBarrier
{
// 创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
// 可以看出在执行完栅栏前面的操作之后,才执行栅栏操作,最后再执行栅栏后边的操作
}//珊栏方法的
.
.
.
.
11、延时方法
- (void)gcdDelyMethod
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"延迟2秒执行--%@",[NSThread currentThread]);
});
}//方法延时
.
.
.
.
12、一次性代码
我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了GCD的dispatch_once方法。使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次
- (void)gcdShareInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"只执行一次--%@",[NSThread currentThread]);
});
}// 一次性代码
.
.
.
.
13、GCD队列组
有时候我们会有这样的需求:分别异步执行2个耗时操作,然后当2个耗时操作都执行完毕后再回到主线程执行操作。这时候我们可以用到GCD的 队列组 ,我们可以先把任务放到队列中,然后将队列放入队列组中,调用队列组的dispatch_group_notify回到主线程执行操作
// 方案一
- (void)queueGroup
{
//队列组
dispatch_group_t group = dispatch_group_create();
//全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//数据容器
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
// 异步任务1(数据请求)
[self showAnimated:YES title:nil requestType:@"POST" file:nil methodName:@"" withParams:@{} completionBlock:^(BOOL b, CGDataResult *dataResult) {
if (b) {
dispatch_group_async(group, queue, ^{
[dict setObject:dataResult forKey:@"data1"];
});
}
} ];
// 异步任务2 (数据请求)
[self showAnimated:YES title:nil requestType:@"POST" file:nil methodName:@"" withParams:@{} completionBlock:^(BOOL b, CGDataResult *dataResult) {
if (b) {
dispatch_group_async(group, queue, ^{
[dict setObject:dataResult forKey:@"data2"];
});
}
} ];
// 执行完成回调
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
id data1 = [dict objectForKey:@"data1"];
id data2 = [dict objectForKey:@"data2"];
});
}//队列组
/*********************************** 方案二 ********************************/
- (void)queueGroup
{
//队列组
dispatch_group_t group = dispatch_group_create();
//数据容器
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
//异步任务1
dispatch_group_enter(group);
[self showAnimated:YES title:nil requestType:@"POST" file:nil methodName:@"" withParams:@{} completionBlock:^(BOOL b, CGDataResult *dataResult) {
if (b) {
[dict setObject:dataResult forKey:@"data1"];
}
dispatch_group_leave(group);
} ];
// 异步任务2
dispatch_group_enter(group);
[self showAnimated:YES title:nil requestType:@"POST" file:nil methodName:@"" withParams:@{} completionBlock:^(BOOL b, CGDataResult *dataResult) {
if (b) {
[dict setObject:dataResult forKey:@"data2"];
}
dispatch_group_leave(group);
} ];
// 执行完成回调
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
id data1 = [dict objectForKey:@"data1"];
id data2 = [dict objectForKey:@"data2"];
});
}
//注意: 方案二 dispatch_group_enter 和 dispatch_group_leave 需要成对出现。
demo地址
并行和并发的区别
并行是指两个或者多个事件在同一时刻发生;
并发是指两个或多个事件在同一时间间隔内发生。