iOS多线程GCD之队列和任务详解(实例代码验证篇)
一、初步了解什么是GCD?
GCD 全称Grand Central Dispatch(队列调度)
是一套低层API,提供了⼀种新的方法来进⾏并发程序编写。从基本功能上讲,GCD有点像NSOperationQueue,他们都允许程序将任务切分为多个单一任务,然后提交⾄⼯作队列来并发的或者串⾏的执行。
GCD是C实现,⽐NSOpertionQueue更底层更高效,并且它不是Cocoa框架的一部分 并发任务会像NSOperationQueue那样基于系统负载来合适地并发进⾏,而串⾏行队列同一时间只执行单一任务。 GCD的API很大程度上基于block
二、GCD如何实现?
GCD主要由队列和任务两部分来实现,许多同学对队列和线程容易搞混,这里把它分类来详述:
1.队列主要包含三种:主线程队列、并行队列、串行队列
2.任务主要包含两种:异步任务和同步任务
多线程执行过程就是把任务放在队列中去执行的过程
(在计算机操作系统中对异步和同步有很深入的概念和定义,并影响到计算机系统有单线程到多线程的过渡发展,其中牵涉到线程切换、时间片等概念。但在GCD这里我们只做大致使用区别)
队列:
并行队列 :在队列中的多个任务(线程) 同时执行 (不按顺序执行)
串行队列: 在队列中的多个任务(线程)排队 依次执行(按顺序执行)
任务:
同步(sync) 和 异步(async) 的主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕!
如果是 同步(sync) 操作,它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行。
如果是 异步(async)操作,当前线程会直接往下执行,它不会阻塞当前线程。
好了 ,不理解也没关系,我在代码中给你来说明和验证;
1、队列 (主要三个队列,为更好理解队列,队列中全部先放入异步任务)
1.主线程队列
主线程队列 内部执行任务是串行的同步操作
主线程队列不需要我们创建,通过dispatch_get_main_queue()方法获得
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
});
或者
dispatch_async(dispatch_get_main_queue(), ^{
});
实例:
-(void)mainQueue{
//异步任务
dispatch_async(dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"主线程任务2_i:%ld",i);
}
});
}
实例输出:
Snip20161012_2.png小结:数据在主线程中按顺序输出
2.创建串行队列
// 创建一个用户串行队列 dispatch_queue_create("com.gcd", NULL),"com.gcd":队列名是一个C字符串,没有特别的要求,Apple建议用倒装的标识符来表示
//串行队列内部也是串行操作
dispatch_async( dispatch_queue_create("com.gcd", NULL), ^{
//任务
});
// 我们自己创建的队列,我们需要自己销毁(非arc 需要销毁)
//dispatch_release(queue);
实例:
-(void)tandemPrivateQueue{
//自定义私有串行队列
dispatch_queue_t queue=dispatch_queue_create("com.privateQueue", NULL);
//异步任务1加入队列中
dispatch_async(queue, ^{
NSLog(@"私有队列任务1");
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"私有队列任务1_i:%ld",i);
}
});
//异步任务2加入队列中
dispatch_async(queue, ^{
NSLog(@"私有队列任务2");
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"私有队列任务2_i:%ld",i);
}
});
}
实例输出:
Snip20161012_3.png小结:私有队列中任务是串行 如输出显示: 先执行任务1完成后->再执行任务2
3.全局队列 并行队列
/*
// 并行队列(全局)不需要我们创建,通过dispatch_get_global_queue()方法获得
// 三个可用队列
// 第一个参数是选取按个全局队列,一般采用DEFAULT,默认优先级队列
// 第二个参数是保留标志,目前的版本没有任何用处(不代表以后版本),直接设置为0就可以了
// DISPATCH_QUEUE_PRIORITY_HIGH
//DISPATCH_QUEUE_PRIORITY_DEFAULT
// DISPATCH_QUEUE_PRIORITY_LOW
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
实例:
-(void)parallelGlobalQueue{
//全局并行队列
dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
NSLog(@"全局队列任务1");
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"全局队列任务1_i:%ld",i);
[NSThread sleepForTimeInterval:0.5];
}
});
dispatch_async(globalQueue, ^{
NSLog(@"全局队列任务2");
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"全局队列任务2_i:%ld",i);
[NSThread sleepForTimeInterval:0.5];
}
});
}
示例输入:
Snip20161012_4.png小结:全局队列中任务是异步同时进行的:任务1和2同时进行
队列到这你该大致明白了 下面再来搞定任务
2、任务
//同步任务:
dispatch_sync(, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
//异步任务:
dispatch_async(, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
同步和异步任务的区别
在上文中队列讲解中所有队列中任务都是异步任务,下面我们在globalQueue队列 把1、2两个异步任务改成同步任务呢,如下面修改 会出现什么情况?
-(void)parallelGlobalQueue{
//并行全局队列
dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//添加一个同步任务1
dispatch_sync(globalQueue, ^{
NSLog(@"全局队列任务1");
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"全局队列任务1_i:%ld",i);
}
});
//添加一个同步任务2
dispatch_sync(globalQueue, ^{
NSLog(@"全局队列任务2");
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"全局队列任务2_i:%ld",i);
}
});
}
输出:
Snip20161012_6.png小结:同是在并行globalQueue队列 通过同步任务和异步任务输出对为什么不同呢?
原因:1和2两个是同步任务,虽然两个任务在并行队列中同时进行,但由同步的原因 任务1会执行完block中所有代码才会继续执行下面的代码,同理任务2也是,就是出现了任务1 输出0-9后才输出任务2。
在此,你明白了异步和同步的区别了吗?
如果是 同步(sync) 操作,它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行。
如果是 异步(async)操作,当前线程会直接往下执行,它不会阻塞当前线程。
关于异步和同步对UI的影响
我们不妨在Main.storyboard中放一个label 看看启动效果即知
Snip20161012_11.png同步任务 会在串/并队列中1、2任务完成输出后显示UIlabel"启动页";
而异步任务 会在串/并队列 先显示出来 在执行1、2任务输出0-9;
可以看出 同步会阻碍主线程的进行,异步不会,所以在许多耗时的操作中放在异步任务来完成后,再返回主线程进行刷新UI
至于队列对UI的影响
这样说吧队列只装载任务并管理任务的顺序,是同时来或一个一个来,有没有影响由队列中任务来决定;但是有种情况也会特殊
-(void)tandemPrivateQueue{
//自定义私有串行队列
dispatch_queue_t queue=dispatch_queue_create("com.privateQueue", NULL);
//异步任务1加入私有队列中
dispatch_async(queue, ^{
NSLog(@"私有队列任务1");
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"私有队列任务1_i:%ld",i);
}
});
//同步任务2加入私有队列中
dispatch_sync(queue, ^{
NSLog(@"私有队列任务2");
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"私有队列任务2_i:%ld",i);
}
});
}
虽然任务1是异步不影响"启动页"的出现,但是如果任务2是同步,"启动页" 会在同步任务2输出完成后方才出现;
这种现象在并行队列中也是如此。
所以不论在串行队列还是并行队列只要有同步任务的出现都会执行完block中代码才会接着往下进行。
总结:到此,就全部结束了!至于队列中嵌套队列,还是任务在嵌套任务,都是万变不离其宗,把基本理解,一起迎刃而解!最后希望和大家一起进步学习!_