title: GCD相关
date: 2019-09-11 10:58:45
tags:
记得之前面试的时候遇到一个问题,一个tableview需要请求多个接口展示数据,如何避免卡顿,当时不太清楚,这里就总结一下GCD相关的一些东西。
#1.简介
GCD是iOS多线程的一种实现方式,GCD会自动管理线程的生命周期,程序员只要告诉GCD要执行什么任务,不需要编写任何线程管理代码
#2.任务
任务就是你在线程中执行的那段代码
#3.队列
GCD有三种队列
3.1主队列
主队列作用跟主线程一样,是串行队列,可通过下面代码获取
let mainQueue = DispatchQueue.main
dispatch_queue_t mainQueue = dispatch_get_main_queue();
3.2全局并发队列
let globalQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
优先级 userInteractive>default>unspecified>userInitiated>utility>background
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
优先级
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
3.3创建队列
let customQueue = DispatchQueue(label: "haha", qos: .default)串行
let customQueue = DispatchQueue(label: "哈哈", qos: .default, attributes: .concurrent)//并发
attributes 默认是串行队列 .concurrent并发队列
dispatch_queue_t customQueue = dispatch_queue_create("哈哈", DISPATCH_QUEUE_SERIAL);
第二个参数 DISPATCH_QUEUE_SERIAL
表示串行队列,DISPATCH_QUEUE_CONCURRENT
表示并发队列。
3.3.1dispatch_set_target_queue
dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);
*第一个参数是要执行变更的队列(不能指定主队列和全局队列)
第二个参数是目标队列(指定全局队列),执行变更的队列优先级跟目标队列变为一样,
链接 https://www.jianshu.com/p/1945f4b8b203
3.4串行队列和并发队列
- 串行队列 :每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
- 可以让多个任务并发(同时)执行。
- 并发队列的并发功能只有在异步(dispatch_async)函数下才有效
#4.任务的创建方法
globalQueue.async { }
globalQueue.sync { }
同步任务不具备开启新线程的能力
4.1同步执行+并发队列
所有任务都是在当前线程(主线程)中执行的,没有开启新的线程
任务按顺序执行的
只有当前线程这一个线程(同步任务
不具备开启新线程的能力),所以也就不存在并发
4.2异步执行 + 并发队列
可以开启多个线程,任务交替(同时)执行
异步执行
不会做任何等待,可以继续执行任务
4.3同步执行 + 串行队列
所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(同步任务
不具备开启新线程的能力
串行队列
每次只有一个任务被执行,任务一个接一个按顺序执行
4.4异步执行 + 串行队列
开启了一条新线程(异步执行
具备开启新线程的能力,串行队列
只开启一个线程)
异步执行
不会做任何等待,可以继续执行任务
任务是按顺序执行的(串行队列
每次只有一个任务被执行,任务一个接一个按顺序执行)
4.5同步执行 + 主队列
4.5.1在主线程中调用
会造成死锁,并且会奔溃
4.5.2在其他线程中调用
Thread.detachNewThread {}
跟 同步执行 + 串行队列 结果一样
4.6异步执行 + 主队列
跟 异步执行 + 串行队列 结果一样
#5.GCD其他方法
5.1barrier栅栏方法
Swift
let customQueue = DispatchQueue(label: "haha", qos: .default, attributes: .concurrent)
customQueue.async(flags: .barrier) {
for _ in 0..<2 {
Thread.sleep(forTimeInterval: 2)
print("barrier--\(Thread.current)")
}
}
OC
dispatch_queue_t queue = dispatch_queue_create("haha", DISPATCH_QUEUE_CONCURRENT);
dispatch_barrier_async(queue, ^{ });
代码执行顺序: barrier前面的代码 ->barrier代码->barrier后面的代码
5.2延时执行
swift
func** after() {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
print("2秒后执行")
}
}
OC
dispatch_after(dispatch_time(DISPATCH_WALLTIME_NOW, 2), dispatch_get_main_queue(), ^{
NSLog(@"两秒后执行");
});
5.2+定时器
Swift
var timer: DispatchSourceTimer?
func timerResume() {
self.timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
timer?.schedule(deadline: .now(), repeating: 1)
timer?.setEventHandler {
DispatchQueue.main.async {[**weak** **self**] **in**
self?.timerCount()
}
}
timer?.resume()
}
func timerCount() {
self.count += 1
print(self.count)
if self.count == 10 {
self.timer?.cancel()
}
}
5.3dispatch_once一次性代码
dispatch_once
函数能保证某段代码在程序运行过程中只被执行1次,并且即使在多线程的环境下,dispatch_once
也可以保证线程安全
OC中创建单例的时候可以用到
+(id)shareInstance {
static ViewController2 *shareInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareInstance = [[ViewController2 alloc]init];
});
return shareInstance;
}
Swift 中已经没有dispatch_once,static背后已经实现了dispatch_once
单例这样写**static** **let** shareInstance = NetworkApi()
5.4dispatch_apply快速迭代
*如果是在串行队列中使用 dispatch_apply
,那么就和 for 循环一样,按顺序同步执行
*可以利用并发队列进行异步执行,dispatch_apply
可以 在多个线程中同时(异步)遍历多个数字。
*dispatch_apply 都会等待全部任务执行完毕,才会进行其他操作
Swift
DispatchQueue.concurrentPerform(iterations: 6) { (index) in
print("\(index),\(Thread.current)")
}
oc
dispatch_queue_t customQueue = dispatch_queue_create("haha", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(6, customQueue, ^(size_t index) {
NSLog(@"%zu", index);
});
5.5dispatch_group队列组
5.5.1dispatch_group_notify
监听 group 中任务的完成状态,当所有的任务都执行完成后,追加任务到 group 中,并执行任务
Swift
let group = DispatchGroup()
let globalQueue = DispatchQueue.global(qos: .default)
globalQueue.async(group: group, qos: .default, flags: DispatchWorkItemFlags(rawValue: 0)) {
for _ in 0..<2 {
Thread.sleep(forTimeInterval: 2)
print("1..\(Thread.current)")
}
}
globalQueue.async(group: group, qos: .default, flags: DispatchWorkItemFlags(rawValue: 0)) {
for _ in 0..<2 {
Thread.sleep(forTimeInterval: 2)
print("2..\(Thread.current)")
}
}
group.notify(queue: DispatchQueue.main) {
for _ in 0..<2 {
Thread.sleep(forTimeInterval: 2)
print("3..\(Thread.current)")
}
}
执行结果
1..<NSThread: 0x600002010800>{number = 3, name = (null)}
2..<NSThread: 0x60000201c080>{number = 4, name = (null)}
1..<NSThread: 0x600002010800>{number = 3, name = (null)}
2..<NSThread: 0x60000201c080>{number = 4, name = (null)}
3..<NSThread: 0x600002072c80>{number = 1, name = main}
3..<NSThread: 0x600002072c80>{number = 1, name = main}
oc
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, globalQueue, ^{
for (int i; i <3; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1...%@",[NSThread currentThread]);
}
});
dispatch_group_async(group, globalQueue, ^{
for (int i; i <3; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2...%@",[NSThread currentThread]);
}
});
dispatch_group_notify(group, globalQueue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"notify... %@",[NSThread currentThread]);
});
5.5.2dispatch_group_wait
暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行,会阻塞当前线程
Swift4
let group = DispatchGroup()
print("groupBegin")
let globalQueue = DispatchQueue.global(qos: .default)
globalQueue.async(group: group, qos: .default, flags: DispatchWorkItemFlags(rawValue: 0)) {
for _ in 0..<2 {
Thread.sleep(forTimeInterval: 2)
print("1..\(Thread.current)")
}
}
globalQueue.async(group: group, qos: .default, flags: DispatchWorkItemFlags(rawValue: 0)) {
for _ in 0..<2 {
Thread.sleep(forTimeInterval: 2)
print("2..\(Thread.current)")
}
}
group.wait()
print("groupEnd")
groupBegin
2..<NSThread: 0x6000038af640>{number = 3, name = (null)}
1..<NSThread: 0x60000389e780>{number = 4, name = (null)}
2..<NSThread: 0x6000038af640>{number = 3, name = (null)}
1..<NSThread: 0x60000389e780>{number = 4, name = (null)}
groupEnd
oc
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
NSLog(@"group begin");
dispatch_group_async(group, globalQueue, ^{
for (int i; i <3; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1...%@",[NSThread currentThread]);
}
});
dispatch_group_async(group, globalQueue, ^{
for (int i; i <3; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2...%@",[NSThread currentThread]);
}
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"group end");
5.6dispatch_semaphore
*dispatch_semaphore_create
:创建一个 Semaphore 并初始化信号的总量
*dispatch_semaphore_signal
:发送一个信号,让信号总量加 1
*dispatch_semaphore_wait
:可以使总信号量减 1
*如果一个动作尝试减少信号量的值,使其小于0,那么这个动作将会被阻塞,直到有其他调用者(在其他线程中)增加该信号量的值。
*通常用作线程同步,保证线程安全
swift
let queue = DispatchQueue.global(qos: .default)
let semaphore = DispatchSemaphore(value: 1)
for i in 0..<100 {
queue.async {
semaphore.wait(timeout: DispatchTime.distantFuture)
print("i=\(i)")
semaphore.signal()
}
}
oc
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_async(queue, ^{
for (int i = 0; i < 100; i ++) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"i = %d", i);
dispatch_semaphore_signal(semaphore);
}
});
参考链接
https://www.jianshu.com/p/2d57c72016c6
https://www.jianshu.com/p/43d6785d97c2
https://developer.apple.com/documentation/dispatch
https://www.jianshu.com/p/7ee4a38c9e27