DispatchQueue 简介

  • DispatchQueue 位于系统的Dispatch库中。 也称为 Grand Central Dispatch (GCD),包含语言功能、运行时库和系统增强功能,为 macOS、iOS、watchOS 和 tvOS 中的多核硬件上的并发代码执行支持提供系统、全面的改进。
  • 继承关系:DispatchQueue -> DispatchObject -> OS_object -> NSObject
  • DispatchQueue 是一个在应用程序的主线程后台线程上管理任务的串行并行的对象。
  • DispatchQueue 是一个 FIFO 队列,您的应用程序可以以块对象 Block的形式向其提交任务。以 串行并行 方式执行 任务
    • 提交到DispatchQueue任务在系统管理的线程池上执行。使用 GCD,您不再直接与线程交互, 除了代表应用程序主线程DispatchQueue外,系统不保证它使用哪个线程来执行任务。

GCD中涉及到了四个专业的名词,串行队列 并发队列 同步执行 异步执行

  • Serial Queue

    • 串行队列, 一次只执行一项任务。串行队列通常用于同步对特定值或资源的访问,以防止发生数据竞争。
  • Concurrent Queue

    • 并发队列可以同时执行多个任务。任务按照它们添加的顺序开始,但可以以不同的顺序完成,因为它们可以并行执行。任务将在由调度队列管理的不同线程上运行。同时运行的任务数量是可变的,取决于系统条件。
  • sync execute

    • 同步执行任务,提交一个任务在当前队列上执行,并在该任务执行完成后返回,阻塞。
  • async execute

    • 异步执行任务,提交一个任务在当前队列上执行,并立即返回,无需等待,非阻塞。

Serial Queue 串行队列

  • 串行调度队列一次只执行一项任务。串行队列通常用于同步对特定值或资源的访问,以防止发生数据竞争。

串行队列-创建

  • 创建串行队列

    1
    2
    // 只传入label默认创建的就是一个串行队列
    let serialQueue = DispatchQueue(label: "com.serial.queue")
  • 系统还为我们提供了一个特殊的串行队列,也叫主队列, 它在主线程在执行任务。

    1
    let mainQueue = DispatchQueue.main

串行队列-执行

同步执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 只传入label默认创建的就是一个串行队列
let serialQueue = DispatchQueue(label: "com.serial.queue")

serialQueue.async {
print("Task 1")
print("Task 2")
}
serialQueue.async {
print("Task 3")
print("Task 4")
}

/* result
Task 1
Task 2
Task 3
Task 4
*/

异步执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let serialQueue = DispatchQueue(label: "com.serial.queue")

serialQueue.sync {
print("Task 1")
print("Task 2")
}
serialQueue.sync {
print("Task 3")
print("Task 4")
}

/* result
Task 1
Task 2
Task 3
Task 4
*/

结论

  • 在串行队列中,任务是一个接一个的执行,下一个任务需要等待上一个任务结束才能执行。

思考:

在串行队列中,任务都是一个接一个的执行的,那么、同步执行和异步执行有什么别呢?

我们将上面的代码连带所在的线程一起打印出来看一下:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
let serialQueue = DispatchQueue(label: "com.serial.queue")

serialQueue.sync {
print("Task 1", Thread.current) // Task 1 <_NSMainThread: 0x600000ba0140>{number = 1, name = main}
}
serialQueue.sync {
print("Task 2", Thread.current)
}

serialQueue.async {
print("Task 3", Thread.current) // Task 3 <NSThread: 0x600001eddc40>{number = 6, name = (null)}

let serialQueue2 = DispatchQueue(label: "com.serial.queue2") // 串行队列2
serialQueue2.sync {
// sync 在 ”当前” 线程,一个接一个的执行
print("Task 3_1", Thread.current) // Task 3_1 <NSThread: 0x6000020d8940>{number = 6, name = (null)}
}
serialQueue2.sync {
print("Task 3_2", Thread.current)
}

serialQueue2.async {
// async 新开启一条线程,一个接一个的执行, 注意:只开一条新线程
print("Task 3_3", Thread.current)
}
serialQueue2.async {
// 还是上面新开启的那条线程哦
print("Task 3_4", Thread.current)
}
}

serialQueue.async {
print("Task 4", Thread.current)
}
serialQueue.async {
print("Task 5", Thread.current)
}

/*
Task 1 <_NSMainThread: 0x600000ba0140>{number = 1, name = main}
Task 2 <_NSMainThread: 0x600000ba0140>{number = 1, name = main}
Task 3 <NSThread: 0x600000bf83c0>{number = 5, name = (null)}
Task 3_1 <NSThread: 0x600000bf83c0>{number = 5, name = (null)}
Task 3_2 <NSThread: 0x600000bf83c0>{number = 5, name = (null)}
/// 注意:下面的四个顺序不是一定如下哦,
/// 原因:
/// 1. 根据串行队列的特点可以确定,Task 4 在 Task 5 前面, Task 3_3 在 Task 3_4 前面
/// 2. 使用了两个串行队列,两个队列之间没有先后绝对的依赖关系。 由于 Task 3_3 Task 3_4 是异步执行,非阻塞立即返回,
Task 4 <NSThread: 0x600000bf83c0>{number = 5, name = (null)}
Task 5 <NSThread: 0x600000bf83c0>{number = 5, name = (null)}
Task 3_3 <NSThread: 0x600000be6200>{number = 6, name = (null)}
Task 3_4 <NSThread: 0x600000be6200>{number = 6, name = (null)}
*/

重点

  • 在串行队列中,使用同步执行,任务会在当前线程中执行。注意:并不一定就是主线程哦, 而是在创建该串行队列时的所在线程。
  • 在串行队列中,使用异步执行,任务会另外开启一条 新的线程 来执行, 不会阻塞当前线程,任务在新线程中一个接一个的执行。注意:只开一条新线程哦。