串行队列(Serial Queue)和并行队列(Concurrent Queue)详解

串行队列(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 3serialQueue 上按顺序执行,Task 2 必须等待 Task 1 完成。
  • async Task 1Task 2 不阻塞主线程,主线程立即打印 "Main thread continues"。
  • sync Task 3 在当前线程(这里是主线程)同步执行,但由于在串行队列上,它仍等待前面的异步任务完成。

2. 并行队列 (Concurrent Queue)

概念

  • 定义: 并行队列允许多个任务同时执行,任务无需等待彼此完成。
  • 特点: 任务并行运行,执行顺序不确定,依赖系统资源和调度。
  • 适用场景:
    • 独立的任务(如网络请求、图像处理)。
    • 需要最大化并发性能的场景。

创建方法

swift 复制代码
let concurrentQueue = DispatchQueue(label: "com.qrlink.concurrent", attributes: .concurrent)
  • 参数:
    • label: 同串行队列,用于标识。
    • attributes: 指定队列属性。
      • .concurrent: 表示创建并行队列。
      • 意义: 使队列允许多任务并发执行。
      • 使用方法: 添加 .concurrent 属性即可。

全局并行队列

  • 系统提供预定义的全局并行队列:

    swift 复制代码
    let 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 1Task 2 完成后执行,确保同步点。
  • globalQueue 使用系统全局队列,适合不需要自定义管理的任务。

3. 串行队列 vs 并行队列

特性 串行队列 并行队列
执行顺序 按顺序(FIFO) 并行,无序
并发性 单任务执行 多任务同时执行
线程安全 天然线程安全 需手动同步(如使用栅栏)
创建方式 DispatchQueue(label:) DispatchQueue(label:, attributes: .concurrent)
典型场景 顺序操作、资源保护 独立任务、高并发

4. 方法与参数详解

DispatchQueue 方法

  1. sync(execute:)

    • 意义: 同步执行任务,阻塞当前线程直到任务完成。

    • 参数: execute: 一个闭包,包含要执行的任务。

    • 使用方法: 用于需要立即获取结果的场景,但避免在主线程使用耗时操作。

    • 示例:

      swift 复制代码
      serialQueue.sync {
          print("Sync task on serial queue")
      }
  2. async(execute:)

    • 意义: 异步执行任务,不阻塞当前线程。

    • 参数: execute: 一个闭包,包含要执行的任务。

    • 使用方法: 适合耗时操作,常见于后台任务。

    • 示例:

      swift 复制代码
      concurrentQueue.async {
          print("Async task on concurrent queue")
      }
  3. async(flags: .barrier, execute:)

    • 意义: 在并行队列中添加栅栏任务,确保前面的任务完成后执行。

    • 参数:

      • flags: .barrier: 指定栅栏属性。
      • execute: 栅栏任务闭包。
    • 使用方法: 用于并行队列中的同步点,例如写操作。

    • 示例:

      swift 复制代码
      concurrentQueue.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() 和其他任务并行执行。
  • 栅栏: 保证关键任务在会话启动后执行。
相关推荐
米西米西5 小时前
iOS/Swift 头像轮播组件
ios
无知的前端5 小时前
一文读懂 iOS 程序生命周期和视图生命周期
ios·面试·swift
CocoaKier5 小时前
Xcode16踩坑:UIApplication.openURL(_:)方法已彻底废弃
ios·xcode·apple
Mr.NickJJ7 小时前
iOS底层原理系列02-深入了解Objective-C
ios·objective-c·cocoa
Swift社区10 小时前
Swift 并发中的任务让步(Yielding)和防抖(Debouncing)
开发语言·ios·swift
#摩斯先生15 小时前
IOS Xcode Could not find a storyboard named ‘Main‘ in bundle NSBundle
macos·ios·xcode
二流小码农1 天前
鸿蒙开发:ArkTs语言注释
android·ios·harmonyos
二流小码农1 天前
鸿蒙开发:权限授权封装
android·ios·harmonyos
JQShan1 天前
iOS符号表:崩溃日志中的“翻译官”,开发中的隐藏高手
面试·debug·swift