串行队列(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() 和其他任务并行执行。
  • 栅栏: 保证关键任务在会话启动后执行。
相关推荐
咕噜签名分发冰淇淋3 小时前
免下载苹果 IPA 文件重签名工具:快速更换应用名称和 BID的教程
ios
二流小码农11 小时前
鸿蒙开发:DevEcoStudio中的代码提取
android·ios·harmonyos
Digitally18 小时前
如何用4 种可靠的方法更换 iPhone(2025 年指南)
ios·iphone
97650333521 小时前
iOS 审核 cocos 4.3a【苹果机审的“分层阈值”设计】
flutter·游戏·unity·ios
I烟雨云渊T21 小时前
iOS Alamofire库的使用
ios
程序员老刘·21 小时前
iOS 26 beta1 真机无法执行hot reload
flutter·ios·跨平台开发·客户端开发
EndingCoder21 小时前
React Native 构建与打包发布(iOS + Android)
android·react native·ios
程序员小刘1 天前
HarmonyOS 5鸿蒙多端编译实战:从Android/iOS到HarmonyOS 5 的跨端迁移指南详
android·ios·华为·harmonyos
I烟雨云渊T1 天前
iOS swiftUI的实用举例
ios·swiftui·swift
大熊猫侯佩1 天前
SwiftUI 中为何 DisclosureGroup 视图在收缩时没有动画效果?
swiftui·swift·apple