30天学会Swift-19:并发编程-GCD与Operation

学习目标

  • 理解并发编程的基本概念和重要性。
  • 掌握Grand Central Dispatch (GCD)的基本用法,包括串行队列、并行队列、主队列和全局队列。
  • 学习如何使用DispatchGroupDispatchSemaphore进行任务同步。
  • 了解OperationOperationQueue的使用,以及它们与GCD的区别。
  • 掌握如何选择合适的并发技术。

学习内容

1. 并发编程简介

并发编程是指在同一时间段内处理多个任务的能力。在移动应用开发中,并发通常用于在后台执行耗时操作(如网络请求、数据处理),以避免阻塞主线程,从而保持用户界面的响应性。

2. Grand Central Dispatch (GCD)

GCD 是苹果提供的一套基于 C 语言的底层并发编程 API,它通过管理调度队列来执行任务。

2.1 调度队列 (Dispatch Queues)
  • 串行队列 (Serial Queues):任务按顺序一个接一个地执行。你可以创建任意数量的串行队列,每个队列中的任务都是串行执行的,但不同串行队列之间的任务可以并行执行。
    let serialQueue = DispatchQueue(label: "com.example.serialQueue")
    serialQueue.async { // 异步执行
        print("Task 1 on serial queue")
    }
    serialQueue.async { // 异步执行
        print("Task 2 on serial queue")
    }
    // 输出顺序不确定,但 Task 1 和 Task 2 在 serialQueue 上是串行执行的
    
  • 并行队列 (Concurrent Queues):任务可以并行执行。任务的开始顺序与添加顺序一致,但完成顺序不确定。
    let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
    concurrentQueue.async { // 异步执行
        for i in 0..<5 { print("Task A: \(i)") }
    }
    concurrentQueue.async { // 异步执行
        for i in 0..<5 { print("Task B: \(i)") }
    }
    // Task A 和 Task B 会并行执行,输出交错
    
  • 主队列 (Main Queue):一个特殊的串行队列,用于执行 UI 更新和所有与用户交互相关的任务。所有 UI 操作都必须在主队列上执行。
    DispatchQueue.main.async { // 异步执行,回到主线程更新 UI
        // Update UI here
        print("Updating UI on main thread")
    }
    
  • 全局队列 (Global Queues):系统提供的并行队列,有不同的优先级(qosquality of service)。
    let globalQueue = DispatchQueue.global(qos: .userInitiated)
    globalQueue.async { // 异步执行
        print("Performing a user-initiated task")
    }
    
2.2 同步与异步
  • async:异步执行,立即返回,不阻塞当前线程。
  • sync:同步执行,阻塞当前线程,直到任务完成。
    • 警告:在主队列上同步执行任务会导致死锁。
2.3 延迟执行
  • asyncAfter:在指定时间后执行任务。
    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { // 2秒后执行
        print("This message appears after 2 seconds")
    }
    
2.4 调度组 (DispatchGroup)

用于在多个任务完成后得到通知。

  • 示例
    let group = DispatchGroup()
    let queue = DispatchQueue.global()
    
    queue.async(group: group) { // 进入组
        print("Task 1 started")
        Thread.sleep(forTimeInterval: 1.0)
        print("Task 1 finished")
    }
    
    queue.async(group: group) { // 进入组
        print("Task 2 started")
        Thread.sleep(forTimeInterval: 2.0)
        print("Task 2 finished")
    }
    
    group.notify(queue: .main) { // 组内所有任务完成后,在主队列通知
        print("All tasks in group finished!")
    }
    
    print("Main thread continues...")
    
2.5 调度信号量 (DispatchSemaphore)

用于控制对共享资源的访问,或限制并发任务的数量。

  • 示例:限制并发任务为 2 个
    let semaphore = DispatchSemaphore(value: 2) // 允许同时有2个任务访问资源
    let queue = DispatchQueue.global()
    
    for i in 1...5 {
        queue.async {
            semaphore.wait() // 信号量减1,如果为负则等待
            print("Task \(i) started")
            Thread.sleep(forTimeInterval: Double.random(in: 0.5...2.0))
            print("Task \(i) finished")
            semaphore.signal() // 信号量加1
        }
    }
    

3. Operation 和 OperationQueue

OperationOperationQueue 是基于 GCD 构建的更高层的抽象,提供了更多的功能,如操作依赖、取消、暂停、恢复等。

3.1 Operation
  • 一个抽象类,代表一个单一的、可执行的任务。

  • 你可以创建 BlockOperation(执行一个或多个闭包)或 Custom Operation(继承 Operation 并重写 main() 方法)。

  • 示例BlockOperation

    let operation1 = BlockOperation { print("Operation 1") }
    let operation2 = BlockOperation { print("Operation 2") }
    
    operation1.start() // 直接启动操作
    operation2.start()
    
3.2 OperationQueue
  • 一个队列,用于管理和调度 Operation 的执行。

  • 可以设置最大并发操作数。

  • 主操作队列OperationQueue.main,在主线程上执行操作。

  • 示例

    let queue = OperationQueue()
    queue.maxConcurrentOperationCount = 3 // 最大并发数
    
    let op1 = BlockOperation { print("Op 1") }
    let op2 = BlockOperation { print("Op 2") }
    let op3 = BlockOperation { print("Op 3") }
    let op4 = BlockOperation { print("Op 4") }
    
    queue.addOperation(op1)
    queue.addOperation(op2)
    queue.addOperation(op3)
    queue.addOperation(op4)
    
    // 添加依赖:op4 在 op1 和 op2 完成后才执行
    op4.addDependency(op1)
    op4.addDependency(op2)
    
    // 取消所有操作
    // queue.cancelAllOperations()
    

4. GCD vs OperationQueue

特性GCDOperationQueue
抽象级别低层级,基于 C 语言高层级,基于 GCD 构建
任务表示闭包 (Blocks)Operation 对象
任务依赖需要手动管理 (如 DispatchGroup)内置支持 (addDependency)
任务取消不直接支持内置支持 (cancel())
任务状态可观察 (isReady, isExecuting, isFinished, isCancelled)
任务暂停/恢复不直接支持内置支持 (isSuspended)
适用场景简单、轻量级任务,对性能要求高复杂任务,需要更多控制和管理

5. 如何选择

  • 使用 GCD
    • 当你需要执行简单的、一次性的任务时。
    • 当你需要对队列和任务的调度有更细粒度的控制时。
    • 当你需要使用 DispatchGroupDispatchSemaphore 进行任务同步时。
  • 使用 OperationQueue
    • 当你需要管理复杂的任务依赖关系时。
    • 当你需要取消、暂停或恢复任务时。
    • 当你需要对任务的执行状态进行观察时。
    • 当你需要限制并发操作的数量时。

实践练习

  1. GCD 队列
    • 创建一个串行队列,异步执行两个任务,观察它们的执行顺序。
    • 创建一个并行队列,异步执行两个任务,观察它们的执行顺序。
    • 在一个后台队列中执行一个耗时操作,然后回到主队列更新 UI(打印一条消息)。
  2. DispatchGroup
    • 模拟两个网络请求,使用DispatchGroup在所有请求完成后打印一条消息。
  3. DispatchSemaphore
    • 模拟一个资源池,限制同时只有3个任务可以访问该资源。
  4. OperationQueue
    • 创建两个BlockOperation,让第二个操作依赖于第一个操作。
    • 将它们添加到OperationQueue中执行,观察执行顺序。
    • 尝试设置maxConcurrentOperationCount来控制并发数。

思考题

  • 为什么 UI 操作必须在主队列上执行?
  • syncasync在不同队列上的行为有何不同?
  • GCDOperationQueue在实际项目中有哪些典型的应用场景?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明似水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值