经过上一个章节,咱们已经了解了一些多线程的基本知识。本章就来说解在平时开发中,最为经常使用的
GCD
的一些知识git
系列文章传送门:程序员
☞ iOS底层学习 - 多线程之基础原理篇github
GCD
全程为Grand Central Dispatch
,由C语言
实现,是苹果为多核的并行运算提出的解决方案,CGD
会自动利用更多的CPU内核,自动管理线程的生命周期,程序员只须要告诉GCD
须要执行的任务,无需编写任何管理线程的代码。GCD
也是iOS使用频率最高的多线程技术。swift
GCD
是苹果公司为多核的并行运算提出的解决方案GCD
会自动利用更多的CPU内核(好比双核、四核)GCD
会自动管理线程的生命周期(建立线程、调度任务、销毁线程)GCD
想要执行什么任务,不须要编写任何线程管理代码GCD
的使用API
较为简单,主要分为同步和异步执行。在上一章咱们对概念已经有所了解,就不作赘述了。多线程
同步执行API并发
//queue:队列 block:任务
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
复制代码
异步执行APIapp
//queue:队列 block:任务
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
复制代码
咱们发如今使用GCD
时,须要传入两个参数。分别是dispatch_queue_t
表明添加的队列类型,dispatch_block_t
为须要执行的任务,下面咱们来看一下不一样参数的运行效果。异步
dispatch_block_t
任务经过查看其定义可得,该参数其实就是一个没有参数的block
回调,用来执行任务的。async
typedef void (^dispatch_block_t)(void);
复制代码
dispatch_queue_t
队列dispatch_queue_t
参数表示队列的类型。根据上一章节咱们知道,队列(FIFO
)在iOS中主要一下分为4种:ide
main_queue
):由系统建立的串行队列。
dispatch_get_main_queue()
global_queue
):由系统建立的并发队列
dispatch_get_global_queue(long identifier, unsigned long flags);
Serial Dispatch Queue
):自定义的串行队列
dispatch_queue_create(@"队列名",DISPATCH_QUEUE_SERIAL)
Concurrent Dispatch Queue
):自定义的并发队列
dispatch_queue_create(@"队列名",DISPATCH_QUEUE_CONCURRENT);
其中,全局队列根据入参的不一样,会获取到不一样的队列,在平常的开发中,咱们通常入参0
,来获取到主并发队列。
自定义的队列都是调用dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr)
方法来进行建立,两个参数分别为自定义队列名称
和队列类型
。其中串行队列DISPATCH_QUEUE_SERIAL
为宏定义的NULL
,因此传NULL
也表示为串行队列。
如今,咱们来看这2种执行模式,3种队列是相互搭配是什么效果
相关代码以下:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"任务1:%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"任务2",[NSThread currentThread]);
});
NSLog(@"任务3:%@",[NSThread currentThread]);
}
复制代码
运行结果
打印出任务1后,程序死锁崩溃
为何会形成以上的结果呢?
首先分析一下代码:主线程执行完任务1
后,在主队列dispatch_get_main_queue()
中同步执行(dispatch_sync)
任务2,而后执行任务3。
队列的特色是FIFO
,主队列中已经存在任务viewDidLoad
,往主队列加入任务2,就须要执行完viewDidLoad
才能执行任务2。可是想要执行完viewDidLoad
又必须先执行viewDidLoad
内的任务2和任务3。这就形成了死锁。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"任务1:%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"任务2:%@",[NSThread currentThread]);
});
NSLog(@"任务3:%@",[NSThread currentThread]);
}
复制代码
运行结果
咱们能够看到当获取到主队列后,使用异步执行的方式,不会形成程序的崩溃。
由于主线程执行任务1以后,须要异步(dispatch_async)
执行任务2;而dispatch_async
不要求立马在当前线程同步执行任务,也就是不会堵塞;因此主线程接着执行任务3,最后异步执行任务2。
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_sync(queue, ^{
for (int i = 0; i<3; i++) {
NSLog(@"任务一:%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i<3; i++) {
NSLog(@"任务二:%@",[NSThread currentThread]);
}
});
}
复制代码
运行结果
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i<3; i++) {
NSLog(@"任务一:%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i<3; i++) {
NSLog(@"任务二:%@",[NSThread currentThread]);
}
});
}
复制代码
运行结果:
能够看出任务1和任务2交错执行,并不是同步执行那样执行完任务1再执行任务2。并且经过线程编号能够看出,的确开启了新的线程。说明异步执行具有开启新线程的能力。
可是经过上面异步+主队列
的打印结果咱们能够发现,在主队列时,异步执行也并无开启新的线程,而仍然是同一个主线程。说明若是在主队列中异步执行,是不会开启新线程的
缘由是由于主队列是串行队列,必须执行完一个任务再执行另外一个任务,异步执行只是没有形成堵塞,可是在主队列仍是是要一步步走的。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"任务1");
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"任务2");
dispatch_sync(queue, ^{
NSLog(@"任务3");
});
NSLog(@"任务4");
});
NSLog(@"任务5");
}
复制代码
运行结果
任务1,任务5,任务2,而后程序崩溃
这里形成死锁崩溃的缘由和上面同步+串行队列
的缘由同样。因为主队列是串行的,因此代码必然是从上到下依次执行的。
打印完任务1后,执行dispatch_async
为异步,不会阻塞线程,可是会加入自定义的串行队列中,因此会执行任务5,接着执行异步代码块中逻辑,打印任务2,接着执行dispatch_sync
代码块,可是因为其是同步,串行队列中已经有了dispatch_async
任务未执行完毕,此时dispatch_sync
阻塞当前线程等待任务3执行,形成了相互等待,因此就死锁崩溃了
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"任务1");
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("myQueue2", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"任务2");
dispatch_sync(queue2, ^{
NSLog(@"任务3");
});
NSLog(@"任务4");
});
NSLog(@"任务5");
}
复制代码
运行结果
任务1,任务5,任务2,任务3,任务4
经过运行咱们能够发现,当嵌套执行时,同步异步在不一样的串行队列执行,并不会形成死锁崩溃。而是按照串行的顺序,执行代码。这事由于dispatch_sync
阻塞的并非当前的线程,而是其余的线程,因此不会形成死锁等待。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"任务1");
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任务2");
dispatch_sync(queue, ^{
NSLog(@"任务3");
});
NSLog(@"任务4");
});
NSLog(@"任务5");
}
复制代码
运行结果:
任务1,任务5,任务2,任务3,任务4
主线程执行任务1以后,须要异步dispatch_async
执行任务2;因此先执行主线程的任务5,而后执行任务2;接着须要在并发队列中同步dispatch_sync
执行任务3,因此会形成阻塞,等待其执行完成,而后执行并发队列中的任务4。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"任务1");
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("myQueue2", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任务2");
dispatch_sync(queue2, ^{
NSLog(@"任务3");
});
NSLog(@"任务4");
});
NSLog(@"任务5");
}
复制代码
运行结果:
任务1,任务5,任务2,任务3,任务4
这个代码的分析和不一样串行队列嵌套
的相似,建立了新的队列,因此是不会形成当前队列的阻塞的。