将类或函数标记为Sendable
可确保它可以安全地跨越并发边界,值类型是安全的,因为它们实现了写时复制等,我们都可以在 Swift 语言文档中找到。但我的观点是,这Sendable
并不保证变量没有数据竞争。即使在基本值类型中,我们也可能存在数据竞争,如Int
下面的代码所示。因此,Sendable
并不等同于数据可以安全地避免并发访问/修改,它只意味着值可以安全地跨不同线程复制。但我的问题是它解决了什么问题,或者作为构造的重要性是什么Sendable
?有人可以解释一下吗?
var num = 0
DispatchQueue.global(qos: .background).async {
for _ in 0..<100 {
num += 1
}
}
DispatchQueue.global(qos: .background).async {
for _ in 0..<100 {
num -= 1
}
}
正如您所说,
Sendable
允许编译器推断某个值是否可以安全地跨并发域发送。这非常有用,因为您经常编写跨并发域发送内容的代码。编译器可以检查您的代码是否安全。如果没有Sendable
,编译器要么需要禁止任何发送(过于严格),要么允许所有发送(不安全)。来自SE-0302:
一个非常简单的例子可以证明
Sendable
它有多么有用:这安全吗?这取决于 的值是否
SomeType
可以安全地发送到顶层Task
。如果发送不安全,您最终可能会SomeType
在 和 它最初来自的地方之间共享的值Task
。例如,如果SomeType
是一个具有可变属性的类,则可能会导致竞争:如果
SomeType
是Sendable
,那么编译器就可以允许第一个代码片段进行编译。以下是来自 SE-0302 的另一个示例:
doThing
对 Actor 来说是孤立的,Actor 可以将string
传递到的存储doThing
在其某个属性中。然后,最终NSMutableString
Actor 和最初拥有它的任何人共享一个。如果我们使用
String
而不是NSMutableString
,那么此代码是安全的,因为String
是Sendable
。String
是写时复制的,因此修改它的参与者不会影响最初拥有它的人。可发送的目的
何时使用 Sendable
示例:符合 Sendable 的自定义类型
示例:将 Sendable 与类一起使用
对于符合 Sendable 的类,其所有存储属性都必须是不可变的,或者本身符合 Sendable。此外,该类必须标记为 final。
如果属性是可变的,则编译器将发出错误,除非您采取明确措施,例如同步访问或确保排他性。