一. GCD 简介
充分利用多核来处理相关任务,它是一个在线程池模式的基础上执行的并发任务
二.GCD 任务和队列
任务:
就是在GCD里的block,执行任务的方式有两种,『同步』和『异步』。
同步执行(sync):
不具备开起线程的能力,同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。
异步执行(async):
具备开起线程的能力,异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务
举个例子:需要打电话给小明和小白。
同步执行:只有一个手机(当前线程),必须等待和小明的同话结束才能打给小白
异步执行:有两个或者多个手机(多个线程),不必等待,用第二个手机打给小白
注意:异步执行(async)虽然具有开启新线程的能力,但是并不一定开启新线程
队列(Dispatch Queue):
用来存放任务的队列,队列都是先进先出的,新的任务被追加到队列的未尾,读取执行的任务都是从队列的开头取出任务
例:即将取出任务1执行【任务1,任务2,任务3】添加新任务4追加到队列末尾,【】代表队列
在 GCD 中有两种队列:『串行队列』 和 『并发队列』。两者的主要区别是:执行顺序不同,以及开启线程数不同。
『串行队列(Serial Dispatch Queue)』:一个任务执行完成才能执行下一个任务
『并发队列(Serial Dispatch Queue)』:多个任务可以同时执行(该能力只在并发队列异步执行下才有)
三.GCD 使用
1.创建队列(串行或并发队列)
2.将任务添加到队列中(同步或异步),然后系统会自动执行队列中的任务
3.1 队列创建和系统已有的队列获取
//串行队列
let syncQueue:DispatchQueue = DispatchQueue.init(label: "SYNC_QUEUE")//默认串行队列
//并发队列
let asyncQqueue:DispatchQueue = DispatchQueue.init(label: "ASYNC_QUEUE", attributes: .concurrent)
//主队列:实质上就是一个普通的串行队列,当前编写的代码都会放在主线程上执行
let mainQueue:DispatchQueue = DispatchQueue.main
//全局并发队列:实质上就是一个普通的并发队列
let globalQueue:DispatchQueue = DispatchQueue.global()
3.2任务创建
queue取决于上面四种队列
//同步执行任务的创建
queue.sync {
//任务的具体实现
}
//异步执行任务的创建
queue.async {
//任务的具体实现
}
四种队列和两种任务执行方式的不同组合如下:
区别如下: 区别图.png同步+串行队列
异步+串行队列
同步+并发队列
异步+并发队列
同步+主队列
异步+主队列
同步+全局队列
异步+全局队列
注意:不能在该串行队列中同步添加任务到该串行队列,否则会造成死锁,
func deadLock(){
/****** 在该串行队列中同步添加任务到该串行队列才会造成死锁 ******/
DispatchQueue.main.sync {//死锁
print("任务1")
}
}
func deadLock(){
/****** 在该串行队列中同步添加任务到该串行队列才会造成死锁 ******/
queueSync.sync {
print("任务1")
self.queueSync.sync {//死锁
print("任务2")
}
print("end")
}
}
死锁原因:串行队列中追加的同步任务1,当同步任务1正执行时,添加同步任务2,此时需要等待任务1执行完毕才能执行任务2,而任务1执行完毕需要等待任务2执行完毕,相互等待,最终死锁,主队列也是如此,死锁这种情况多见于同一个串行队列的嵌套使用。
以下是几种不同组合的例子:
同步异步+串行队列
/**
* 同步执行 + 串行队列
* 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。
*/
func syncSerial(){
print("currentThread---\(Thread.current)")
print("begin")
let queue:DispatchQueue = DispatchQueue.init(label: "syncSerial")
queue.sync {
sleep(4)
print("1---\(Thread.current)")
}
queue.sync {
sleep(2)
print("2---\(Thread.current)")
}
queue.sync {
print("3---\(Thread.current)")
}
print("end")
}
输出:currentThread---<NSThread: 0x170071c80>{number = 1, name = main}
begin
1---<NSThread: 0x170071c80>{number = 1, name = main}
2---<NSThread: 0x170071c80>{number = 1, name = main}
3---<NSThread: 0x170071c80>{number = 1, name = main}
end
同步(sync)执行任务不具有开启线程的能力,所以共用一个主线程,一个任务执行完毕才能执行下一个任务
/**
* 异步执行 + 串行队列
* 特点:开启一条新线程,执行完一个任务,再执行下一个任务,主线程不再等待
*/
func asyncSerial(){
print("currentThread---\(Thread.current)")
print("begin")
let queue:DispatchQueue = DispatchQueue.init(label: "asyncSerial")
queue.async {
sleep(4)
print("1---\(Thread.current)")
}
queue.async {
sleep(2)
print("2---\(Thread.current)")
}
queue.async {
print("3---\(Thread.current)")
}
print("end")
}
currentThread---<NSThread: 0x174074880>{number = 1, name = main}
begin
end
1---<NSThread: 0x1702684c0>{number = 3, name = (null)}
2---<NSThread: 0x1702684c0>{number = 3, name = (null)}
3---<NSThread: 0x1702684c0>{number = 3, name = (null)}
异步(async)执行任务具有开启新线程的能力,且主线程不等待输出end,但串行队列只开启一条线程,且任务一个执行完毕才能执行下一个
同步异步+并发队列
/**
* 同步执行 + 并发队列
* 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。
*/
func syncConcurrent(){
print("currentThread---\(Thread.current)")
print("begin")
let queue:DispatchQueue = DispatchQueue.init(label: "syncConcurrent", attributes: .concurrent)
queue.sync {
sleep(4)
print("1---\(Thread.current)")
}
queue.sync {
sleep(2)
print("2---\(Thread.current)")
}
queue.sync {
print("3---\(Thread.current)")
}
print("end")
}
>输出:
>currentThread---<NSThread: 0x1700730c0>{number = 1, name = main}
>begin
>1---<NSThread: 0x1700730c0>{number = 1, name = main}
>2---<NSThread: 0x1700730c0>{number = 1, name = main}
>3---<NSThread: 0x1700730c0>{number = 1, name = main}
>end
同步(sync)执行任务不具有开启线程的能力,并发列队只在异步(async)执行任务的时候才会开启新的线程且并发执行任务,所以当前共用一个线程(主线程),同步(sync)执行任务需要一个执行完毕才能执行下一个
/**
* 异步执行 + 并发队列
* 特点:队列开启新线程执行任务,主线程不再等待
*/
func asyncConcurrent(){
print("asyncConcurrent---\(Thread.current)")
print("begin")
let aqueue:DispatchQueue = DispatchQueue.init(label: "asyncConcurrent", attributes: .concurrent)
aqueue.async {
sleep(3)
print("1---\(Thread.current)")
}
aqueue.async {
sleep(2)
print("2---\(Thread.current)")
}
aqueue.async {
sleep(1)
print("3---\(Thread.current)")
}
print("end")
}
>输出:asyncConcurrent---<NSThread: 0x17006f740>{number = 1, name = main}
>begin
>end
>3---<NSThread: 0x170263840>{number = 3, name = (null)}
>2---<NSThread: 0x170263ac0>{number = 4, name = (null)}
>1---<NSThread: 0x170263b80>{number = 5, name = (null)}
异步(async)执行任务具有开启线程能力,所以当前主线程不等待继续执行下面代码输出end,并发队列开启新的线程并在该线程中执行任务
同步异步+主队列
/**
* 同步执行 + 主队列
* 特点:死锁
*/
func syncMain(){
print("syncMain---\(Thread.current)")
print("begin")
DispatchQueue.main.sync {
//任务1
print("1---\(Thread.current)")
}
print(" end")
}
syncMain---<NSThread: 0x17006dec0>{number = 1, name = main}
begin
(lldb)
主队列是一个串行队列。这里是因为当系统执行syncMain方法,相当于把syncMain任务放到主队列里,而执行syncMain的过程中添加了任务1到主队列中,syncMain执行完毕需要待任务1执行完,任务1执行完需要等待syncMain执行完,互相等待,所以死锁
/**
* 异步执行 + 主队列
* 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。
*/
func asyncMain(){
print("asyncMain---\(Thread.current)")
print("begin")
let aqueue:DispatchQueue = DispatchQueue.main
aqueue.async {
sleep(3)
print("1---\(Thread.current)")
}
aqueue.async {
sleep(2)
print("2---\(Thread.current)")
}
aqueue.async {
sleep(1)
print("3---\(Thread.current)")
}
print("end")
}
asyncMain---<NSThread: 0x174067300>{number = 1, name = main}
begin
end
1---<NSThread: 0x174067300>{number = 1, name = main}
2---<NSThread: 0x174067300>{number = 1, name = main}
3---<NSThread: 0x174067300>{number = 1, name = main}
异步执行不会做任何等待,可以继续执行任务,虽然异步执行可以开启新的线程,但主队列是串行队列本身已存在主线程,所以此处不再开启新的线程。
全局并发队列跟普通并发队列并无区别
相关简书:
iOS GCD学习总结(二)
iOS 线程同步方案学习总结
信号量semaphore学习总结
iOS dispatch_barrier_sync实现多读单写
NSOperation和NSOperationQueue学习总结
网友评论