首页 Swift中的并发
文章
取消

Swift中的并发

并发编程基础

并发编程是一种常见的编程方式,它允许应用程序同时处理多个任务或操作。并发编程在多核处理器和分布式系统中尤为重要,因为它可以更有效地利用资源,并提高应用程序的性能。

在并发编程中,你需要处理共享资源(如内存、文件等)以及多个线程可能同时访问这些资源所带来的竞争条件问题。为了避免这些问题,你需要使用特殊的工具和技术来确保线程安全。

多线程的概念

多线程是指在同一时间内执行多个线程的技术。每个线程可以独立运行,并有自己的堆栈和指令指针。线程之间可以共享数据和代码,但是必须小心处理共享资源以避免竞态条件。

在 iOS 应用程序中,多线程通常用于异步加载数据、执行长时间运行的操作或与后台服务进行交互。下面将介绍几个常用的并发编程工具。

GCD(Grand Central Dispatch)

GCD(Grand Central Dispatch) 是一个用于管理队列的 C 语言库。它提供了简单易用的 API,使得在应用程序中使用多线程变得非常容易。GCD 使用“队列”来管理任务,并可根据优先级和执行时间对队列进行排序。

队列和任务

队列是任务的集合,它定义了一组规则来管理它们的执行。你可以创建串行队列(按顺序执行任务)或并发队列(同时执行多个任务)。你还可以将任务添加到队列中,并设置它们的优先级和执行时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建并发队列
let queue = DispatchQueue(label: "com.example.myqueue", attributes: .concurrent)

// 将任务添加到队列中
queue.async {
    // 执行任务
}

// 创建串行队列
let serialQueue = DispatchQueue(label: "com.example.serialqueue")

// 将任务添加到队列中
serialQueue.sync {
    // 执行任务
}

延迟执行

GCD 还提供了一种延迟执行任务的方法。你可以使用 DispatchQueue.main.asyncAfter(deadline:) 方法来延迟执行任务。该方法接受一个 DispatchTime 参数,指示任务应在何时执行。

1
2
3
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
    // 延迟 1 秒后执行任务
}

主队列

主队列是一个串行队列,用于执行应用程序的 UI 操作。主队列始终在主线程上执行任务,因此你可以放心地更新 UI 元素或响应用户事件。

1
2
3
DispatchQueue.main.async {
    // 在主线程上执行任务
}

NSOperationQueue

NSOperationQueue 是另一个用于管理队列的 Cocoa 框架。它提供了比 GCD 更高级的功能,例如依赖关系和取消操作。使用 NSOperation Queue,你可以将操作添加到队列中,并根据需要配置它们的属性。

操作和依赖关系

操作是任务的抽象表示,它定义了需要执行的代码块。你可以创建一个 NSOperation 子类来自定义操作。然后,你可以将操作添加到队列中,并使用 addDependency(_:) 方法为它们设置依赖关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let operation1 = BlockOperation {
    // 执行操作 1
}

let operation2 = BlockOperation {
    // 执行操作 2
}

// 设置操作 1 的依赖关系
operation2.addDependency(operation1)

// 将操作添加到队列中
let queue = OperationQueue()
queue.addOperations([operation1, operation2], waitUntilFinished: false)

在上面的示例中,操作 1 必须在操作 2 之前执行。因此,我们使用 addDependency(_:) 方法将操作 2 的依赖关系设置为操作 1。

取消操作

NSOperationQueue 还提供了一种取消操作的方法。你可以在操作或队列级别上调用 cancel() 方法来取消操作。如果你要取消正在执行的操作,则需要在操作中定期检查取消状态。

1
2
3
4
5
6
7
let operation = BlockOperation {
    if self.isCancelled {
        return
    }
    
    // 执行操作
}

在上面的示例中,我们在操作中检查 isCancelled 属性,以确定是否应该停止执行操作。

异步函数

Swift 5.5 引入了异步函数的概念。异步函数是指会立即返回并启动一个新的任务,在完成时再回调结果的函数。这使得编写异步代码变得更加容易和直观。

使用异步函数,你可以通过 async/await 语法轻松地处理异步代码。下面是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func getData() async throws -> Data {
    let url = URL(string: "https://example.com/data")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}

async {
    do {
        let data = try await getData()
        // 处理数据
    } catch {
        // 处理错误
    }
}

在上面的示例中,我们定义了一个异步函数 getData(),它使用 URLSession 获取数据并返回一个 Data 对象。然后,我们使用 async/await 语法在异步块中调用该函数。

并发值

Swift 还引入了 async letActor 等功能,用于处理并发访问共享资源的问题。使用 async let,你可以轻松地获取多个异步值,并等待它们全部准备就绪后再继续执行。

1
2
3
4
async let data1 = fetchRemoteData()
async let data2 = fetchLocalData()

let result = await (data1, data2)

在上面的示例中,我们使用 async let 获取两个异步值 data1data2。然后,我们使用 await 等待两个值全部准备就绪后,再将它们作为元组返回给 result 变量。

Actor

Actor 是一种用于保护共享状态的新类型。使用 Actor,你可以确保访问共享状态的唯一途径是通过 Actor 提供的 API。这可以避免竞态条件和其他常见的并发问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
actor Counter {
    private var count = 0
    
    func increment() {
        count += 1
    }
    
    func getCount() -> Int {
        return count
    }
}

let counter = Counter()

Task.detached {
    for _ in 0..<100 {
        counter.increment()
    }
}

在上面的示例中,我们定义了一个名为 Counter 的 Actor。它有一个私有变量 count,并提供了两个方法 incrementgetCount 来访问它。

然后,我们创建了一个名为 counter 的实例,并在异步任务中调用 100 次 increment 方法。由于 Actor 是线程安全的,因此我们不必担心竞态条件或其他问题。

本文由作者按照 CC BY 4.0 进行授权

设计模式:面向对象设计的六大原则

如何在Xcode中使用Copilot