我正在嘗試使一個類符合Sendable. 我有一些導致問題的可變存盤屬性。但是,我無法理解的是 MainActor 隔離屬性不允許我的類符合 Sendable。但是,如果我將整個班級標記為 a @MainActor,那就沒問題了。但是,我實際上并不想讓整個班級都符合@MainActor.
舉個例子,拿這段代碼:
final class Article: Sendable {
@MainActor var text: String = "test"
}
它給出了這個警告:Stored property 'text' of 'Sendable'-conforming class 'Article' is mutable。
有人可以解釋為什么嗎?我認為與演員隔離會很好。
uj5u.com熱心網友回復:
該錯誤警告您您的類正在公開可變屬性。該可變屬性可以從 Swift 并發之外訪問,因此不安全。
考慮以下:
final class Foo: Sendable {
@MainActor var counter = 0 // Stored property 'counter' of 'Sendable'-conforming class 'Foo' is mutable
}
無論如何,我們現在可以考慮視圖控制器的以下屬性和方法,它們直接互動counter:
let foo = Foo()
func incrementFooManyTimes() {
DispatchQueue.global().async { [self] in
DispatchQueue.concurrentPerform(iterations: 10_000_000) { _ in
foo.counter = 1
}
print(foo.counter) // 6146264 !!!
}
}
注意:如果您確實將“Swift Concurrency Checking”構建設定設定為“Minimal”或“Targeted”,則以上內容將僅在上述警告的情況下進行編譯。(如果你把它改成“Complete”,它就變成了一個硬錯誤。)
無論如何,簡而言之,如果你已經標記了 as @MainActor,但是沒有什么可以阻止其他執行緒直接與類的這個屬性互動。
如果您要讓非Sendable參與者具有可變屬性,則必須自己實作執行緒安全。例如:
final class Foo: @unchecked Sendable {
private var _counter = 0
private let queue: DispatchQueue = .main // I would use `DispatchQueue(label: "Foo.sync")`, but just illustrating the idea
var counter: Int { queue.sync { _counter } }
func increment() {
queue.sync { _counter = 1 }
}
}
和
func incrementFooManyTimes() {
DispatchQueue.global().async { [self] in
DispatchQueue.concurrentPerform(iterations: 10_000_000) { _ in
foo.increment()
}
print(foo.counter) // 10000000
}
}
顯然,您也可以限制自己使用不可變屬性,并且不需要同步。但我假設你需要可變性。
現在,在這個可變場景中,您可以使用任何您想要的同步機制,但希望這能說明這個想法。簡而言之,如果你打算讓它在 Swift 并發之外發生變化,你必須自己實作同步。因為我們正在實作我們自己的同步,所以我們告訴編譯器它是@unchecked,這意味著你不會讓編譯器檢查它的正確性,而是這個負擔落在你的肩上。
顯然,如果您使用 actor 并留在 Swift 并發世界中,生活會容易得多。例如:
actor Bar {
var counter = 0
func increment() {
counter = 1
}
}
和:
let bar = Bar()
func incrementBarManyTimes() {
Task.detached {
await withTaskGroup(of: Void.self) { group in
for _ in 0 ..< 10_000_000 {
group.addTask { await self.bar.increment() }
}
await print(self.bar.counter)
}
}
}
uj5u.com熱心網友回復:
閱讀 Swift Evolution 提案,似乎Sendable尚未設計/實施復雜的一致性檢查。
從全球演員提案中,據說標有全球演員的型別隱式符合Sendable。
用全域參與者注釋的非協議型別隱式符合
Sendable. 這種型別的實體可以安全地跨并發域共享,因為對其狀態的訪問受到全域參與者的保護。
: Sendable所以如果你用 ! 標記你的最后一堂課,你甚至不需要@MainActor!
另一方面,提案提到Sendable:
類的可發送一致性檢查
[...] 一個類可能符合 Sendable 并在特定的有限情況下由編譯器檢查記憶體安全性:當該類是最終類時僅包含符合 Sendable 的型別的不可變存盤屬性:
final class MyClass : Sendable { let state: String }
let基本上,如果您不使用全域參與者標記您的最終課程,則您課程中的每個存盤屬性都必須是 a 。
我無法在這兩份檔案中找到任何其他內容,或其他有關 Swift Evolution 的相關提案。
所以目前的設計甚至不關心你是否添加@MainActor到一個屬性。最終類符合的兩個充分條件Sendable
該Sendable提案還提到:
未來有幾種方法可以概括這一點,但有一些不明顯的案例需要確定。因此,該提案有意限制類的安全檢查,以確保我們在并發設計的其他方面取得進展。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/534967.html
標籤:迅速快速并发
