引言
Swift 的并发模型旨在帮助开发者编写更加安全和高效的并发代码。通过引入 Sendable 协议和 @Sendable 属性,Swift 为确保在任务和 actor 之间传递数据时的安全性提供了强有力的支持。理解和正确使用这些特性,对于编写健壮的并发程序至关重要。
什么是 Sendable 类型
swift 并发模型追求的一个关键目标是-提供一种在并发编程中隔离状态的机制,以消除数据竞争。
Task和actor将程序分成可以安全并发的部分。每一个Task或 actor 的实例内部被称为并发域。某些类型的数据无法在并发域之间共享,因为这些数据包含可变状态,但它们不能防止数据竞争。
可以从一个并发域共享到另一个并发域数据类型,被称为Sendable类型。它可以作为参数在调用 actor 方法时传递,或者作为任务的结果返回。
Sendable 协议
我们可以让我们的自定义类型遵循Sendable 协议。该协议没有任何代码要求,但它确实有 Swift 强制执行的语义要求。一般来说,有三点语义要求。
-
该类型是值类型,并且他的可变状态也是
Sendable类型struct School: Sendable { var numberOfStudents: Int } -
该类型没有可变状态,且它的不可变状态也是
Sendable类型final class Student: Sendable { let name = "xxx" let age = 20 } -
该类型具有确保其可变状态安全的代码,例如,一个标记为 @MainActor 的类,或一个在特定线程或队列上序列化其属性访问的类。
@MainActor class MyViewModel: Sendable { var studentName: String init(studentName: String) { self.studentName = studentName } } // 这个类自己实现了防止数据竞争机制,编译不再检查语义要求 class Counter:@unchecked Sendable { // 同步队列 private let queue = DispatchQueue(label: "serial") private var value = 0 func increment() { queue.async { self.value += 1 } } }
@Sendable 属性
我们使用@Sendable标注闭包,使得闭包可以在多个并发域中安全传递。并且对于闭包捕获有下列要求:
- 闭包不应该按值捕获(
capture by value)任何非 Sendable 类型。 - 闭包不应该按引用捕获(
capture by reference)任何类型。
class A {
var v1 = 10
func f() {
var v2 = 20
let v3 = 30
// extension Task where Failure == Never {
//@discardableResult
//public init(priority: TaskPriority? = nil, operation: @escaping @Sendable () async -> Success)
//}
Task {
// error: apture of 'self' with non-sendable type 'A' in a `@Sendable` closure
print(v1)
// error: Reference to captured var 'v2' in concurrently-executing code
print(v2)
// valid: Captured by value and v3 is Sendable type.
print(v3)
}
}
}
结语
Swift 的 Sendable 协议和 @Sendable 属性为并发编程提供了重要的工具,确保了在多任务环境中数据的安全传递和使用。通过遵循 Sendable 协议和合理使用 @Sendable 属性,我们可以显著降低数据竞争的风险,从而编写更加可靠和高效的并发程序。
如果您有任何问题,请评论区告诉我。