使用GCD前,先对队列总结一下:
串行和并发
GCD中队列分为两种:串行队列、并发队列。
串行队列:任务按在队列里的先后顺序执行,先进先出(FIFO),一个任务执行完成后,才会执行下一个任务(实际实现是在一个线程中完成所有任务,这个线程可能是主线程,也可能是子线程)。
并发队列:任务无序同时执行(实际实现是在多个子线程中执行)。
同步和异步
任务的执行方式分为同步和异步两种,同步和异步是以是否在当前线程执行区分的。
同步执行:任务都在当前线程中执行,并且会阻塞当前线程。
异步执行:任务会开辟新的线程,并在新的线程中执行,不会阻塞当前线程。
组合四种队列
两种队列和两种执行方式组合起来就形成了四种队列:串行同步队列、串行异步队列、并发同步队列、并发异步队列。四种队列区别如下(先贴结论,后面验证):
串行同步队列:任务都在当前线程执行(同步),并且顺序执行(串行)
串行异步队列:任务都在开辟的新的子线程中执行(异步),并且顺序执行(串行)
并发同步队列:任务都在当前线程执行(同步),但是是顺序执行的(并发的特性并没有体现出来)
并发异步队列:任务在开辟的多个子线程中执行(异步),并且是同时执行的(并发)
注:由上面的特性即可看出,我们平时对GCD使用最多的是并发异步队列。
准备工作
研究队列类型之间的区别前,先看一下GCD中生成队列的方法:
方法一:dispatch_queue_create:可以生成串行和并发队列
// 前两个方法生成的是串行队列;第三个生成的是并发队列
dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_create("com.serial.queue", NULL);
dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
方法二:dispatch_get_global_queue:只能生成并发队列
// 只能生成并发队列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
验证四种队列
打印方法:
#pragma mark - private methods
- (void)p_printThread:(int)index {
NSLog(@"%@-------%d", [NSThread currentThread], index);
}
1.串行同步队列
dispatch_queue_t queue = dispatch_queue_create("com.serial.queue", NULL);
dispatch_sync(queue, ^(){
[self p_printThread:1];
});
dispatch_sync(queue, ^(){
[self p_printThread:2];
});
dispatch_sync(queue, ^(){
[self p_printThread:3];
});
dispatch_sync(queue, ^(){
[self p_printThread:4];
});
打印结果:
<NSThread: 0x608000068380>{number = 1, name = main}-------1
<NSThread: 0x608000068380>{number = 1, name = main}-------2
<NSThread: 0x608000068380>{number = 1, name = main}-------3
<NSThread: 0x608000068380>{number = 1, name = main}-------4
结论:在串行同步队列中,任务都在当前线程执行(同步),并且顺序执行(串行)。
注:打印的结果都是主线程,是因为当前线程是主线程,这并不是说同步就是在主线程执行的。
2.串行异步队列
dispatch_queue_t queue = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^(){
[self p_printThread:1];
});
dispatch_async(queue, ^(){
[self p_printThread:2];
});
dispatch_async(queue, ^(){
[self p_printThread:3];
});
dispatch_async(queue, ^(){
[self p_printThread:4];
});
打印结果:
<NSThread: 0x60800007b440>{number = 3, name = (null)}-------1
<NSThread: 0x60800007b440>{number = 3, name = (null)}-------2
<NSThread: 0x60800007b440>{number = 3, name = (null)}-------3
<NSThread: 0x60800007b440>{number = 3, name = (null)}-------4
结论:在串行异步队列中,任务都在新开辟的子线程中执行(异步),并且顺序执行(串行)。
3.并发同步队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queue, ^(){
[self p_printThread:1];
});
dispatch_sync(queue, ^(){
[self p_printThread:2];
});
dispatch_sync(queue, ^(){
[self p_printThread:3];
});
dispatch_sync(queue, ^(){
[self p_printThread:4];
});
打印结果:
<NSThread: 0x6080000790c0>{number = 1, name = main}-------1
<NSThread: 0x6080000790c0>{number = 1, name = main}-------2
<NSThread: 0x6080000790c0>{number = 1, name = main}-------3
<NSThread: 0x6080000790c0>{number = 1, name = main}-------4
结论:在并发同步队列中,任务都在当前线程中执行(同步),但是是顺序执行的(并发的特性并没有体现出来,因为同步会阻塞当前线程)。
4.并发异步队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^(){
[self p_printThread:1];
});
dispatch_async(queue, ^(){
[self p_printThread:2];
});
dispatch_async(queue, ^(){
[self p_printThread:3];
});
dispatch_async(queue, ^(){
[self p_printThread:4];
});
打印结果:
<NSThread: 0x618000066700>{number = 4, name = (null)}-------2
<NSThread: 0x60000006cc80>{number = 6, name = (null)}-------4
<NSThread: 0x60000006ccc0>{number = 3, name = (null)}-------1
<NSThread: 0x6080000635c0>{number = 5, name = (null)}-------3
结论:在并发异步队列中,任务在多个新开辟的子线程中执行(异步),并且是同时无序地执行的(并发)
分析总结
由上面代码示例可以看出,串行队列就是在一个线程执行的队列:如果是当前线程,那就是串行同步队列(因为同步机制会阻塞当前线程),如果是新开辟的子线程,就是串行异步队列;并发队列如果是在当前线程中执行,就是并发同步队列,在新开辟的多个子线程中执行就是并发异步队列。
综上所述,我们平时使用最多的自然是并发异步队列,比如开辟多个子线程下载图片、文件等。