GCD(Grand Central Dispatch)是苹果推出的用于多核并行编程的技术.关于 GCD 编程一定要知道的知识点:
1、GCD 是什么:GCD 是一种异步执行任务的技术,能够帮助程序员更方便地实现多线程编程,提高程序的性能。
2、GCD 的优势:GCD 可以通过自动化任务的管理来避免开发者手动管理线程和队列,从而减少代码复杂度和错误率。
3、GCD 中的队列:GCD 中有两种队列,串行队列和并发队列。串行队列每次只能执行一个任务,而并发队列可以同时执行多个任务。
4、GCD 中的任务:GCD 中的任务可以是同步任务或异步任务。同步任务会阻塞当前线程,直到该任务完成为止,而异步任务会在后台执行,不会阻塞当前线程。
5、GCD 中的执行方法:GCD 提供了三种任务执行方法,分别是同步执行、异步执行和栅栏执行。同步执行是在当前线程中执行任务,异步执行是在后台线程中执行任务,而栅栏执行可以在任务执行前和执行后插入其他任务。
6、GCD 中的 Dispatch Group:Dispatch Group 可以将一组任务添加到一个组中,然后等待组中的所有任务执行完成后再执行其他操作。
7、GCD 中的 Dispatch Semaphore:Dispatch Semaphore 可以用来管理资源的访问,可以控制同时访问资源的线程数。
8、GCD 中的 Dispatch Source:Dispatch Source 可以用来监控各种系统事件,如文件系统变化、定时器事件等等。
在GCD中,队列是用于管理任务的一种机制。队列可以分为两种类型:
不同类型的队列在任务执行的方式上有很大不同。
串行队列按照任务添加到队列中的顺序,一个一个地执行,每次只有一个任务在执行。也就是说,任务按照FIFO的顺序执行。在串行队列中,任务必须一个接一个地执行,因此这种队列适用于需要顺序执行任务的场景,比如在某个任务完成后执行另一个任务。
并发队列可以同时执行多个任务,但是任务的执行顺序并不一定按照添加到队列中的顺序,而是取决于系统资源的可用性和任务的优先级。在并发队列中,任务是并发执行的,所以适用于需要同时执行多个任务的场景,比如图片下载或者数据处理。
主队列是一个串行队列,负责在主线程上执行任务,因此在主队列上执行的任务必须要快速完成,避免阻塞主线程。自定义队列可以是串行队列或并发队列,开发者可以根据自己的需求创建自定义队列。
同步执行是指任务必须等待前面的任务完成后才能开始执行,任务会阻塞当前线程,因此同步执行适用于需要顺序执行任务的场景。
异步执行是指任务会立即在队列中排队,不会阻塞当前线程,只有当系统资源可用时才会执行任务,因此适用于需要异步执行任务的场景。
在 GCD 中,任务是执行工作的基本单元。可以将任务想象为一些代码块,这些代码块可以在一个队列中按照一定的顺序依次执行,或者可以在多个队列中并行执行。
GCD 中的任务有两种类型:
同步任务是指在当前线程中执行的任务,会等待任务执行完成之后再继续执行下一个任务。同步任务通常使用串行队列执行。
异步任务是指在后台线程中执行的任务,不会阻塞当前线程,并且可以并发执行多个任务。异步任务通常使用并发队列执行。
GCD中的任务可以用闭包、函数或代码块的形式表示。以下是几个示例:
使用闭包表示一个异步任务:
DispatchQueue.global().async {
// 异步任务代码
}
使用函数表示一个同步任务:
let queue = DispatchQueue(label: "com.example.serialQueue")
queue.sync {
// 同步任务代码
}
使用代码块表示一个异步任务:
let queue = DispatchQueue.global(qos: .userInitiated)
queue.async(execute: {
// 异步任务代码
})
在GCD中,任务可以添加到队列中,然后GCD会自动管理任务的执行。
可以将任务添加到串行队列中,以确保它们按照添加的顺序依次执行。也可以将任务添加到并发队列中,以便它们可以并发执行。
当使用GCD时,需要注意以下几点:
当我们使用GCD创建队列后,可以向队列中添加任务,让它们在队列中依次执行。GCD提供了多种方法来添加任务,常用的包括以下几种:
let queue = DispatchQueue(label: "com.example.myqueue")
queue.sync {
// 这里执行任务
}
let queue = DispatchQueue(label: "com.example.myqueue")
queue.async {
// 这里执行任务
}
let queue = DispatchQueue(label: "com.example.myqueue")
let group = DispatchGroup()
queue.async(group: group) {
// 这里执行任务
}
let queue = DispatchQueue(label: "com.example.myqueue", attributes: .concurrent)
queue.async {
// 这里执行任务
}
queue.async {
// 这里执行任务
}
queue.barrierAsync {
// 这里执行屏障任务
}
queue.async {
// 这里执行任务
}
queue.async {
// 这里执行任务
}
let workItem = DispatchWorkItem {
// 这里执行任务
}
let queue = DispatchQueue(label: “com.example.myqueue”)
queue.async(execute: workItem)
以上这些是GCD中添加任务的常用方法,可以根据实际情况选择适合自己的方法。同时,GCD还提供了其他一些方法,如DispatchQueue.global()、DispatchQueue.main等,可以根据不同的需求选择合适的方法。
DispatchQueue.main
和 DispatchQueue.global
都是 DispatchQueue
的静态属性,用于获取全局的主队列和后台队列,是 GCD 最常用的两个队列。
DispatchQueue.main
是 iOS 应用程序中的主队列,主要用于在主线程上执行任务,因为在 iOS 应用程序中,只有主线程才能更新 UI 和处理用户交互。所有在 DispatchQueue.main
中执行的任务都将在主线程上执行,如果在主线程中执行较耗时的任务,可能会导致 UI 卡顿,因此需要将这些任务放在后台线程中执行。
DispatchQueue.global
是 GCD 提供的全局队列,它会自动管理线程池,可根据需要动态创建或销毁线程。DispatchQueue.global
提供四种不同的 QoS(Quality of Service)服务质量服务等级:.userInteractive
(用户交互)、.userInitiated
(用户主动发起)、.default
(默认)、.utility
(实用工具)。每个等级代表不同的执行优先级和处理资源,可以根据任务的不同特性和优先级,选择合适的 QoS。
当在 DispatchQueue.global
中执行任务时,它们将在后台线程中执行,不会阻塞主线程,因此,执行耗时任务时,应该优先选择在 DispatchQueue.global
中执行。
// 在主队列上执行任务
DispatchQueue.main.async {
// 更新 UI
}
// 在后台队列中执行任务
DispatchQueue.global().async {
// 执行耗时操作
}
// 在后台队列中执行带有 QoS 的任务
DispatchQueue.global(qos: .userInitiated).async {
// 执行用户主动发起的任务
}
注意,使用 DispatchQueue.global
时,我们应该尽量避免阻塞主线程,否则会导致 UI 卡顿,影响用户体验。同时,在使用 DispatchQueue.global
时,也应该合理选择 QoS 等级,以确保任务得到合适的执行优先级和处理资源。
思考樂:调用DispatchQueue.global时,会无限创建新的queque嘛?
不会。调用 DispatchQueue.global 方法时,会返回一个全局共享的 queue。
GCD会根据应用程序的需要和系统资源的状况自动创建一定数量的 queue,并根据需要在这些 queue 中调度任务,而不是无限创建新的queue。这些 queue 通常是线程池,可以被多个任务共享,并且会在任务执行完毕后自动重用。
直接调用DispatchQueue.global 还是自己创建新的Queue好?
在处理并发任务时,可以选择直接使用 DispatchQueue.global,也可以创建自己的 queue。它们各有优缺点,具体取决于应用程序的需求。
直接使用 DispatchQueue.global 的优点是:
自己创建新的 queue 的优点是:
如何高性能的使用DispatchQueue.global?
使用DispatchQueue.global创建并发队列时,建议使用预定义的QoS类别,如.userInitiated、.utility、.background。这些类别对应了不同的优先级,以及系统默认的并发线程数,可以使得在并发执行任务时更加高效地利用CPU资源。
另外,可以结合使用DispatchWorkItem和DispatchGroup,来实现更高效的任务调度和处理。比如,可以将多个任务包装在一个DispatchGroup中,通过notify(queue:)方法来等待所有任务执行完成,也可以通过wait(timeout:)方法来阻塞当前线程,等待任务执行完成。
在使用DispatchQueue.global时,需要注意任务之间的依赖关系,避免出现死锁或者竞争条件的情况。同时,也需要注意避免在并发执行任务时出现资源抢占的情况,比如多个线程同时对同一个资源进行修改时,需要使用DispatchSemaphore等同步机制来保证资源的正确性。