iOS-多线程GCD

1、GCD(Grand Central Dispatch)

⚠️:不要在主线程中执行同步Swiftync操作,回产生死锁

优点:

  1. 易用:比thread更简单
  2. 效率:轻量,比消耗资源的线程更实用快捷
  3. 性能:根据系统负载自动增减线程数量,减少了上下文切换
  4. 安全:无须加锁和其他同步机制

同步和异步:

  • 同步sync:只能在当前线程中执行任务,不能开启新的线程
  • 异步async:可以在新的线程中执行任务,可以开启新的线程

异步回主线程:

1
2
3
4
5
6
DispatchQueue.global().async {
print("异步做某事:\(Thread.current)")
DispatchQueue.main.async {
print("回到住线程:\(Thread.current)")
}
}

2、DispatchQueue

FIFO队列,背后是一个由系统管理的线程池

1
2
3
4
5
6
let queue = DispatchQueue(
label: "labelname", //队列的标识符
qos: .default, //quality of service,优先级
attributes: .concurrent, //属性
autoreleaseFrequency: .inherit //自动释放频率
)

串行和并行

1
2
3
4
5
6
7
8
9
/*
默认:列队是串行的
.concurrent:列队是并发的
.initiallyInactive:列队不会自动执行,需要开发中手动触发
*/
// 串行队列
let serialQueue = DispatchQueue(label: "serialQueue")
// 并行队列
let concurrentQueue = DispatchQueue(label: "concurrentQueue",attributes:.concurrent)
1
2
3
4
5
6
// 读数据
func readDataTask(label: String){
print("\(Thread.current) Start sync task\(label)")
sleep(2)
print("\(Thread.current) End sync task\(label)")
}

串行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func serial() {
let serialQueue = DispatchQueue(label: "serialQueue")
serialQueue.async {
readDataTask(label: "1")
}
serialQueue.async {
readDataTask(label: "2")
}
}
/*
<NSThread: 0x7fa4b84043d0>{number = 2, name = (null)} Start sync task1
<NSThread: 0x7fa4b84043d0>{number = 2, name = (null)} End sync task1
<NSThread: 0x7fa4b84043d0>{number = 2, name = (null)} Start sync task2
<NSThread: 0x7fa4b84043d0>{number = 2, name = (null)} End sync task2
*/

并行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func concurrent() {
//并行
print("concurrentQueue:")
let concurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)
concurrentQueue.async {
readDataTask(label: "3")
}
concurrentQueue.async {
readDataTask(label: "4")
}
}
/*
<NSThread: 0x7fa4b84043d0>{number = 2, name = (null)} Start sync task3
<NSThread: 0x7fa4b8204b10>{number = 4, name = (null)} Start sync task4
<NSThread: 0x7fa4b84043d0>{number = 2, name = (null)} End sync task3
<NSThread: 0x7fa4b8204b10>{number = 4, name = (null)} End sync task4
*/

异步与同步

同步 (sync)

提交一段任务到队列,并且阻塞当前线程,任务结束后当前线程继续执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func sync() {
print("sync:")
print("\(Thread.current) Main queue Start")
let concurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)
concurrentQueue.sync {
readDataTask(label: "1")
}
concurrentQueue.sync {
readDataTask(label: "2")
}
print("\(Thread.current) Main queue End")
}
/*
<NSThread: 0x7fba4b40e7e0>{number = 1, name = main} Main queue Start
<NSThread: 0x7fba4b40e7e0>{number = 1, name = main} Start sync task1
<NSThread: 0x7fba4b40e7e0>{number = 1, name = main} End sync task1
<NSThread: 0x7fba4b40e7e0>{number = 1, name = main} Start sync task2
<NSThread: 0x7fba4b40e7e0>{number = 1, name = main} End sync task2
<NSThread: 0x7fba4b40e7e0>{number = 1, name = main} Main queue End
*/

异步(async)

提交一段任务到队列,并且立刻返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func async() {
print("\(Thread.current) Main queue Start")
let concurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)
concurrentQueue.async {
readDataTask(label: "3")
}
concurrentQueue.async {
readDataTask(label: "4")
}
print("\(Thread.current) Main queue End")
}
async()
/*
<NSThread: 0x7fc2c7a045e0>{number = 1, name = main} Main queue Start
<NSThread: 0x7fc2c7c10d70>{number = 2, name = (null)} Start sync task3
<NSThread: 0x7fc2c6709010>{number = 3, name = (null)} Start sync task4
<NSThread: 0x7fc2c7a045e0>{number = 1, name = main} Main queue End
<NSThread: 0x7fc2c6709010>{number = 3, name = (null)} End sync task4
<NSThread: 0x7fc2c7c10d70>{number = 2, name = (null)} End sync task3
*/

asyncAfter 延迟执行

1
2
3
4
5
6
7
queue.async(group: group, qos: .default, flags: []) {
DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
for _ in 0...4 {
print("\(Thread.current) 耗时任务一")
}
})
}

DispatchQoS (quality of service) 服务质量

任务的服务质量或执行优先级

  • background:最低优先级,等同于 DISPATCH_QUEUE_PRIORITY_BACKGROUND. 用户不可见,比如:在后台存储大量数据
  • utility:优先级等同于 DISPATCH_QUEUE_PRIORITY_LOW,可以执行很长时间,再通知用户结果。比如:下载一个大文件,网络,计算
  • default:默认优先级,优先级等同于 DISPATCH_QUEUE_PRIORITY_DEFAULT,建议大多数情况下使用默认优先级
  • userInitiated:优先级等同于 DISPATCH_QUEUE_PRIORITY_HIGH,需要立刻的结果
  • userInteractive:用户交互相关,为了好的用户体验,任务需要立马执行。使用该优先级用于 UI 更新,事件处理和小工作量任务,在主线程执行
1
2
3
DispatchQueue.global().async(qos: .background) {
// code
}

DispatchWorkItem

把任务封装为一个对象

1
2
3
4
let item = DispatchWorkItem {
// 任务
}
DispatchQueue.global().async(execute: item)

也可指定更多参数:

1
2
3
DispatchWorkItem(qos: .userInitiated, flags: [.assignCurrentContext,.barrier]) {
// 任务
}

DispatchWorkItemFlags

指定任务的额外信息

1
2
3
4
5
6
public static let barrier: 
public static let detached:
public static let assignCurrentContext:
public static let noQoS: //没有QoS
public static let inheritQoS: //继承Queue的QoS
public static let enforceQoS: //自己的QoS覆盖Queue的QoS

Barrier 屏障

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func barrier() {
let queue = DispatchQueue(label: "barrier", attributes: .concurrent)
queue.async {
print("任务A")
}
// 这里 barrier,必须等任务B完成后,才走后面任务C
queue.async(flags: .barrier) {
sleep(2)
print("任务B")
}
queue.async {
print("任务C")
}
}
1
2
3
4
5
// 保证写入时,不能读数据
let item = DispatchWorkItem(qos: .default, flags: .barrier) {
// write data
}
DispatchQueue().async(execute: item)

DispatchGroup

管理一组任务的执行,然后监听任务的完成,进而执行后续操作

notify

等group中的任务完成后,执行后续任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func groupNotify() {
let queue = DispatchQueue.global()
let group = DispatchGroup()
queue.async(group: group, qos: .default, flags: [], execute: {
for _ in 0...4 {
print("\(Thread.current) 耗时任务一")
}
})
queue.async(group: group, qos: .default, flags: [], execute: {
for _ in 0...4 {
print("\(Thread.current) 耗时任务二")
}
})
// 执行完上面的两个耗时操作, 回到 queue 队列中执行下一步的任务
group.notify(queue: queue) {
print("\(Thread.current) 回到该队列中执行")
}
}

wait

等一段时间之后,不管任务有没有完成,都继续执行后续任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func groupWait() {
let queue = DispatchQueue.global()
let group = DispatchGroup()
queue.async(group: group, qos: .default, flags: [], execute: {
for _ in 0...4 {
print("\(Thread.current) 耗时任务一")
}
})
queue.async(group: group, qos: .default, flags: [], execute: {
for _ in 0...4 {
print("\(Thread.current) 耗时任务二")
sleep(1)
}
})
// 等待上面任务执行,会阻塞当前线程,超时就执行下面的,上面的继续执行。可以无限等待 .distantFuture
let result = group.wait(timeout: .now()+10)
switch result {
case .success:
print("\(Thread.current) 不超时, 上面的两个任务都执行完")
case .timedOut:
print("\(Thread.current) 超时了, 上面的任务还没执行完执行这了")
}
print("\(Thread.current) 完成...")
}

enter 和 leave

group.enter()表示一个任务被加入到group中,此时group中任务的引用计数会加1

group.leave()表示group中的一个任务完成,引用计数减1

当引用计数为0时,回通知notify函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func enterLeaveGroup() {
let group = DispatchGroup()
let queue = DispatchQueue.global()

// 把该任务添加到组队列中执行
group.enter()
queue.async(group: group, qos: .default, flags: []) {
// 增加耗时
DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
for _ in 0...4 {
print("\(Thread.current) 耗时任务一")
}
// 执行完之后从组队列中移除
group.leave()
})
}

// 把该任务添加到组队列中执行
group.enter()
queue.async(group: group, qos: .default, flags: []) {
for _ in 0...4 {
print("\(Thread.current) 耗时任务二")
}
// 执行完之后从组队列中移除
group.leave()
}

// 当上面所有的任务执行完之后通知
group.notify(queue: queue) {
print("\(Thread.current) 所有的任务执行完了")
}
}

3、DispatchSemaphore 信号量

当信号计数大于0时,每条进来的线程使计数减1,直到变为0,变为0后其他的线程将进不来,处于等待状态;

执行完任务的线程释放信号,使计数加1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func semaphore() {
// 创建信号量,参数:信号量的初值,如果小于0则会返回 NULL, value 表示最多几个资源可访问
let semaphore = DispatchSemaphore(value: 2)
let queue = DispatchQueue.global()
// 任务1
queue.async {
// 等待降低信号量
semaphore.wait()
print("运行任务1")
sleep(1)
print("结果任务1")
// 提高信号量
semaphore.signal()
}
// 任务2
queue.async {
// 等待降低信号量
semaphore.wait()
print("运行任务2")
sleep(1)
print("结果任务2")
// 提高信号量
semaphore.signal()
}
}

Suspend 和 Resume

  • Suspend 可以挂起一个线程,即暂停线程,但是仍然暂用资源,只是不执行
  • Resume 回复线程,即继续执行挂起的线程。

循环执行任务 concurrentPerform

1
2
3
4
// 并发执行5次
DispatchQueue.concurrentPerform(iterations: 5) {
print("\($0)")
}

DispatchSource

处理特定的系统底层事件,当一些特定的系统底层事件发生时,调度源会捕捉到这些事件,然后可以做相应的逻辑处理。

  • Timer Dispatch Source:定时调度源。
  • Signal Dispatch Source:监听UNIX信号调度源,比如监听代表挂起指令的SIGSTOP信号。
  • Descriptor Dispatch Source:监听文件相关操作和Socket相关操作的调度源。
  • Process Dispatch Source:监听进程相关状态的调度源。
  • Mach port Dispatch Source:监听Mach相关事件的调度源。
  • Custom Dispatch Source:监听自定义事件的调度源。
0%