串行队列(Serial Queue)和并行队列(Concurrent Queue)详解并代码示例。内容主要基于 DispatchQueue
,是 Grand Central Dispatch (GCD) 的核心组件,用于管理多线程任务。
1. 串行队列 (Serial Queue)
概念
- 定义: 串行队列是一种任务按顺序执行的队列,后续任务必须等待前一个任务完成后才能开始。
- 特点: 任务以 FIFO(先进先出)顺序执行,保证同一时间只有一个任务在运行。
- 适用场景:
- 需要保证任务顺序的操作(如文件写入、数据库操作)。
- 避免竞争条件(Race Condition),确保线程安全。
创建方法
swift
let serialQueue = DispatchQueue(label: "com.qrlink.serial")
- 参数:
label
: 队列的标识符(字符串),用于调试和标识队列,建议使用反向域名格式。- 意义: 在 Xcode 调试或 Instruments 中区分队列。
- 使用方法: 例如
"com.qrlink.serial"
。
- 默认属性: 创建的是串行队列,除非指定其他参数。
使用方法
- 同步执行:
sync
- 在当前线程阻塞执行任务。
- 异步执行:
async
- 不阻塞当前线程,任务在队列中排队执行。
代码示例与注解
swift
import UIKit
class SerialQueueExample {
// 创建一个串行队列
private let serialQueue = DispatchQueue(label: "com.qrlink.serialQueue")
func performTasks() {
// 任务 1: 异步执行
serialQueue.async {
print("Task 1 started at \(Date())")
Thread.sleep(forTimeInterval: 2) // 模拟耗时操作
print("Task 1 finished at \(Date())")
}
// 任务 2: 异步执行
serialQueue.async {
print("Task 2 started at \(Date())")
Thread.sleep(forTimeInterval: 1) // 模拟耗时操作
print("Task 2 finished at \(Date())")
}
// 任务 3: 同步执行
serialQueue.sync {
print("Task 3 started at \(Date())")
Thread.sleep(forTimeInterval: 1) // 模拟耗时操作
print("Task 3 finished at \(Date())")
}
print("Main thread continues at \(Date())")
}
}
// 测试代码
let example = SerialQueueExample()
example.performTasks()
输出示例:
arduino
Main thread continues at 2025-03-18 10:00:00 +0000
Task 1 started at 2025-03-18 10:00:00 +0000
Task 1 finished at 2025-03-18 10:00:02 +0000
Task 2 started at 2025-03-18 10:00:02 +0000
Task 2 finished at 2025-03-18 10:00:03 +0000
Task 3 started at 2025-03-18 10:00:03 +0000
Task 3 finished at 2025-03-18 10:00:04 +0000
注解:
- 串行性:
Task 1
,Task 2
,Task 3
在serialQueue
上按顺序执行,Task 2
必须等待Task 1
完成。 async
:Task 1
和Task 2
不阻塞主线程,主线程立即打印 "Main thread continues"。sync
:Task 3
在当前线程(这里是主线程)同步执行,但由于在串行队列上,它仍等待前面的异步任务完成。
2. 并行队列 (Concurrent Queue)
概念
- 定义: 并行队列允许多个任务同时执行,任务无需等待彼此完成。
- 特点: 任务并行运行,执行顺序不确定,依赖系统资源和调度。
- 适用场景:
- 独立的任务(如网络请求、图像处理)。
- 需要最大化并发性能的场景。
创建方法
swift
let concurrentQueue = DispatchQueue(label: "com.qrlink.concurrent", attributes: .concurrent)
- 参数:
label
: 同串行队列,用于标识。attributes
: 指定队列属性。.concurrent
: 表示创建并行队列。- 意义: 使队列允许多任务并发执行。
- 使用方法: 添加
.concurrent
属性即可。
全局并行队列
-
系统提供预定义的全局并行队列:
swiftlet globalQueue = DispatchQueue.global(qos: .userInitiated)
- 参数:
qos
: 服务质量(Quality of Service),决定任务优先级。.userInitiated
: 用户发起的任务,高优先级。.userInteractive
: UI 相关任务,最高优先级。.utility
: 长时间运行的任务,中等优先级。.background
: 后台任务,低优先级。- 意义: 控制任务执行的资源分配和优先级。
- 使用方法: 根据任务类型选择,例如
.userInitiated
用于快速响应用户操作。
- 参数:
使用方法
- 同步执行:
sync
- 阻塞当前线程,直到任务完成。
- 异步执行:
async
- 不阻塞当前线程,任务并行执行。
- barrier(栅栏):
async(flags: .barrier)
- 在并行队列中创建同步点,前面的任务完成后执行栅栏任务,完成后继续并行。
代码示例与注解
swift
import UIKit
class ConcurrentQueueExample {
// 创建一个自定义并行队列
private let concurrentQueue = DispatchQueue(label: "com.qrlink.concurrentQueue", attributes: .concurrent)
// 使用全局并行队列
private let globalQueue = DispatchQueue.global(qos: .userInitiated)
func performTasks() {
// 任务 1: 异步执行
concurrentQueue.async {
print("Task 1 started at \(Date()) on \(Thread.current)")
Thread.sleep(forTimeInterval: 2)
print("Task 1 finished at \(Date())")
}
// 任务 2: 异步执行
concurrentQueue.async {
print("Task 2 started at \(Date()) on \(Thread.current)")
Thread.sleep(forTimeInterval: 1)
print("Task 2 finished at \(Date())")
}
// 任务 3: 使用栅栏,确保前面的任务完成后执行
concurrentQueue.async(flags: .barrier) {
print("Barrier Task started at \(Date())")
Thread.sleep(forTimeInterval: 1)
print("Barrier Task finished at \(Date())")
}
// 任务 4: 在全局队列执行
globalQueue.async {
print("Global Task started at \(Date())")
Thread.sleep(forTimeInterval: 1)
print("Global Task finished at \(Date())")
}
print("Main thread continues at \(Date())")
}
}
// 测试代码
let example = ConcurrentQueueExample()
example.performTasks()
输出示例:
yaml
Main thread continues at 2025-03-18 10:00:00 +0000
Task 1 started at 2025-03-18 10:00:00 +0000 on <NSThread: 0x...>{number = 2}
Task 2 started at 2025-03-18 10:00:00 +0000 on <NSThread: 0x...>{number = 3}
Global Task started at 2025-03-18 10:00:00 +0000 on <NSThread: 0x...>{number = 4}
Task 2 finished at 2025-03-18 10:00:01 +0000
Global Task finished at 2025-03-18 10:00:01 +0000
Task 1 finished at 2025-03-18 10:00:02 +0000
Barrier Task started at 2025-03-18 10:00:02 +0000
Barrier Task finished at 2025-03-18 10:00:03 +0000
注解:
- 并行性:
Task 1
,Task 2
, 和Global Task
同时开始(不同线程),无需等待彼此完成。 async
: 不阻塞主线程,主线程立即继续。.barrier
: 栅栏任务等待Task 1
和Task 2
完成后执行,确保同步点。globalQueue
: 使用系统全局队列,适合不需要自定义管理的任务。
3. 串行队列 vs 并行队列
特性 | 串行队列 | 并行队列 |
---|---|---|
执行顺序 | 按顺序(FIFO) | 并行,无序 |
并发性 | 单任务执行 | 多任务同时执行 |
线程安全 | 天然线程安全 | 需手动同步(如使用栅栏) |
创建方式 | DispatchQueue(label:) |
DispatchQueue(label:, attributes: .concurrent) |
典型场景 | 顺序操作、资源保护 | 独立任务、高并发 |
4. 方法与参数详解
DispatchQueue 方法
-
sync(execute:)
-
意义: 同步执行任务,阻塞当前线程直到任务完成。
-
参数:
execute
: 一个闭包,包含要执行的任务。 -
使用方法: 用于需要立即获取结果的场景,但避免在主线程使用耗时操作。
-
示例:
swiftserialQueue.sync { print("Sync task on serial queue") }
-
-
async(execute:)
-
意义: 异步执行任务,不阻塞当前线程。
-
参数:
execute
: 一个闭包,包含要执行的任务。 -
使用方法: 适合耗时操作,常见于后台任务。
-
示例:
swiftconcurrentQueue.async { print("Async task on concurrent queue") }
-
-
async(flags: .barrier, execute:)
-
意义: 在并行队列中添加栅栏任务,确保前面的任务完成后执行。
-
参数:
flags: .barrier
: 指定栅栏属性。execute
: 栅栏任务闭包。
-
使用方法: 用于并行队列中的同步点,例如写操作。
-
示例:
swiftconcurrentQueue.async(flags: .barrier) { print("Barrier task") }
-
DispatchQueue 参数
label
: 队列名称。- 意义: 调试时标识队列。
- 示例:
"com.qrlink.myQueue"
。
qos
: 服务质量。- 选项:
.userInteractive
,.userInitiated
,.utility
,.background
。 - 意义: 控制任务优先级和资源分配。
- 选项:
attributes
: 队列属性。- 选项:
.concurrent
,.initiallyInactive
。 - 意义:
.concurrent
创建并行队列,.initiallyInactive
创建需手动激活的队列。
- 选项:
5. 综合应用示例
结合摄像头扫描场景,展示串行和并行队列的使用:
swift
class CameraQueueExample {
private let serialQueue = DispatchQueue(label: "com.qrlink.serialCamera")
private let concurrentQueue = DispatchQueue(label: "com.qrlink.concurrentCamera", attributes: .concurrent)
private var captureSession: AVCaptureSession?
func setupCamera() {
captureSession = AVCaptureSession()
guard let captureSession = captureSession else { return }
// 串行队列:顺序配置摄像头
serialQueue.async {
guard let device = AVCaptureDevice.default(for: .video) else { return }
do {
let input = try AVCaptureDeviceInput(device: device)
captureSession.beginConfiguration()
captureSession.addInput(input)
let output = AVCaptureMetadataOutput()
captureSession.addOutput(output)
captureSession.commitConfiguration()
// 并行队列:同时启动会话和处理其他任务
self.concurrentQueue.async {
captureSession.startRunning()
print("Camera started at \(Date())")
}
self.concurrentQueue.async {
print("Other task started at \(Date())")
Thread.sleep(forTimeInterval: 1)
print("Other task finished at \(Date())")
}
// 栅栏:确保会话启动后再执行
self.concurrentQueue.async(flags: .barrier) {
print("Barrier task after camera setup at \(Date())")
}
} catch {
print("Setup failed: \(error)")
}
}
}
}
let cameraExample = CameraQueueExample()
cameraExample.setupCamera()
输出示例:
yaml
Camera started at 2025-03-18 10:00:00 +0000
Other task started at 2025-03-18 10:00:00 +0000
Other task finished at 2025-03-18 10:00:01 +0000
Barrier task after camera setup at 2025-03-18 10:00:01 +0000
注解:
- 串行队列: 确保摄像头配置按顺序完成。
- 并行队列:
startRunning()
和其他任务并行执行。 - 栅栏: 保证关键任务在会话启动后执行。