Swift 并发模型:Sendable 类型

260 阅读3分钟

引言

Swift 的并发模型旨在帮助开发者编写更加安全和高效的并发代码。通过引入 Sendable 协议和 @Sendable 属性,Swift 为确保在任务和 actor 之间传递数据时的安全性提供了强有力的支持。理解和正确使用这些特性,对于编写健壮的并发程序至关重要。

什么是 Sendable 类型

swift 并发模型追求的一个关键目标是-提供一种在并发编程中隔离状态的机制,以消除数据竞争。

Taskactor将程序分成可以安全并发的部分。每一个Taskactor 的实例内部被称为并发域。某些类型的数据无法在并发域之间共享,因为这些数据包含可变状态,但它们不能防止数据竞争。

可以从一个并发域共享到另一个并发域数据类型,被称为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 属性,我们可以显著降低数据竞争的风险,从而编写更加可靠和高效的并发程序。

如果您有任何问题,请评论区告诉我。