成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Swift 中的 Actors 使用以如何及防止數(shù)據(jù)競(jìng)爭(zhēng)

移動(dòng)開(kāi)發(fā) iOS
Swift 中的 Actors 旨在完全解決數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題,但重要的是要明白,很可能還是會(huì)遇到數(shù)據(jù)競(jìng)爭(zhēng)。本文將介紹 Actors 是如何工作的,以及你如何在你的項(xiàng)目中使用它們。

前言

Swift Actors 是 Swift 5.5 中的新內(nèi)容,也是 WWDC 2021 上并發(fā)重大變化的一部分。在有 actors 之前,數(shù)據(jù)競(jìng)爭(zhēng)是一個(gè)常見(jiàn)的意外情況。因此,在我們深入研究具有隔離和非隔離訪問(wèn)的行為體之前,最好先了解什么是數(shù)據(jù)競(jìng)爭(zhēng)[1],并了解當(dāng)前你如何解決這些問(wèn)題[2]。

Swift 中的 Actors 旨在完全解決數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題,但重要的是要明白,很可能還是會(huì)遇到數(shù)據(jù)競(jìng)爭(zhēng)。本文將介紹 Actors 是如何工作的,以及你如何在你的項(xiàng)目中使用它們。

什么是 Actors?

Swift 中的 Actor 并不新鮮:它們受到 Actor Model[3] 的啟發(fā),該模型將行為視為并發(fā)計(jì)算的通用基元。然后,SE-0306[4]提案引入了 Actor,并解釋了它們解決了哪些問(wèn)題:數(shù)據(jù)競(jìng)爭(zhēng)。

當(dāng)多個(gè)線程在沒(méi)有同步的情況下訪問(wèn)同一內(nèi)存,并且至少有一個(gè)訪問(wèn)是寫(xiě)的時(shí)候,就會(huì)發(fā)生數(shù)據(jù)競(jìng)爭(zhēng)。數(shù)據(jù)競(jìng)爭(zhēng)會(huì)導(dǎo)致不可預(yù)測(cè)的行為、內(nèi)存損壞、不穩(wěn)定的測(cè)試和奇怪的崩潰。你可能會(huì)遇到無(wú)法解決的崩潰,因?yàn)槟悴恢浪鼈兒螘r(shí)發(fā)生,如何重現(xiàn)它們,或者如何根據(jù)理論來(lái)修復(fù)它們。

Swift 中的 Actors 可以保護(hù)他們的狀態(tài)免受數(shù)據(jù)競(jìng)爭(zhēng)的影響,并且使用它們可以讓編譯器在編寫(xiě)應(yīng)用程序時(shí)為我們提供有用的反饋。此外,Swift 編譯器可以靜態(tài)地強(qiáng)制執(zhí)行 Actors 附帶的限制,并防止對(duì)可變數(shù)據(jù)的并發(fā)訪問(wèn)。

您可以使用 actor 關(guān)鍵字定義一個(gè) Actor,就像您使用類(lèi)或結(jié)構(gòu)體一樣:

actor ChickenFeeder {
let food = "worms"
var numberOfEatingChickens: Int = 0
}

Actor 和其他 Swift 類(lèi)型一樣,它們也可以有初始化器、方法、屬性和子標(biāo)號(hào),同時(shí)你也可以用協(xié)議和泛型來(lái)使用它們。此外,與結(jié)構(gòu)體不同的是:當(dāng)你定義的屬性需要手動(dòng)定義時(shí),actor 需要自定義初始化器。最后,重要的是要認(rèn)識(shí)到 actor 是引用類(lèi)型。

Actor 是引用類(lèi)型,但與類(lèi)相比仍然有所不同

Actor 是引用類(lèi)型,簡(jiǎn)而言之,這意味著副本引用的是同一塊數(shù)據(jù)。因此,修改副本也會(huì)修改原始實(shí)例,因?yàn)樗鼈冎赶蛲粋€(gè)共享實(shí)例。你可以在我的文章Swift 中的 Struct 與 class 的區(qū)別中了解更多這方面的信息。

然而,與類(lèi)相比,Actor 有一個(gè)重要的區(qū)別:他們不支持繼承。

圖片

Swift中的Actor幾乎和類(lèi)一樣,但不支持繼承。

Swift 中的 Actor 幾乎和類(lèi)一樣,但不支持繼承。

不支持繼承意味著不需要像便利初始化器和必要初始化器、重寫(xiě)、類(lèi)成員或 open? 和 final 語(yǔ)句等功能。

然而,最大的區(qū)別是由 Actor 的主要職責(zé)決定的,即隔離對(duì)數(shù)據(jù)的訪問(wèn)。?

如何防止數(shù)據(jù)競(jìng)爭(zhēng)

Actors 如何通過(guò)同步來(lái)防止數(shù)據(jù)競(jìng)爭(zhēng)。

Actor 通過(guò)創(chuàng)建對(duì)其隔離數(shù)據(jù)的同步訪問(wèn)來(lái)防止數(shù)據(jù)競(jìng)爭(zhēng)。在Actors之前,我們會(huì)使用各種鎖來(lái)創(chuàng)建相同的結(jié)果。這種鎖的一個(gè)例子是并發(fā)調(diào)度隊(duì)列與處理寫(xiě)訪問(wèn)的屏障相結(jié)合。受我在Concurrent vs. Serial DispatchQueue: Concurrency in Swift explained[5]一文中解釋的技術(shù)的啟發(fā)。我將向你展示使用 Actor 的前后對(duì)比。

在 Actor 之前,我們會(huì)創(chuàng)建一個(gè)線程安全的小雞喂食器,如下所示:

final class ChickenFeederWithQueue {
let food = "worms"

/// 私有支持屬性和計(jì)算屬性的組合允許同步訪問(wèn)。
private var _numberOfEatingChickens: Int = 0
var numberOfEatingChickens: Int {
queue.sync {
_numberOfEatingChickens
}
}

/// 一個(gè)并發(fā)的隊(duì)列,允許同時(shí)進(jìn)行多次讀取。
private var queue = DispatchQueue(label: "chicken.feeder.queue", attributes: .concurrent)

func chickenStartsEating() {
/// 使用柵欄阻止寫(xiě)入時(shí)的讀取
queue.sync(flags: .barrier) {
_numberOfEatingChickens += 1
}
}

func chickenStopsEating() {
/// 使用柵欄阻止寫(xiě)入時(shí)的讀取
queue.sync(flags: .barrier) {
_numberOfEatingChickens -= 1
}
}
}

正如你所看到的,這里有相當(dāng)多的代碼需要維護(hù)。在訪問(wèn)非線程安全的數(shù)據(jù)時(shí),我們必須仔細(xì)考慮自己使用隊(duì)列的問(wèn)題。需要一個(gè)柵欄標(biāo)志來(lái)停止讀取并允許寫(xiě)入。再一次,我們需要自己來(lái)處理這個(gè)問(wèn)題,因?yàn)榫幾g器并不強(qiáng)制執(zhí)行它。最后,我們?cè)谶@里使用了一個(gè) DispatchQueue,但是經(jīng)常有圍繞著哪個(gè)鎖是最好的爭(zhēng)論。

為了看清這一點(diǎn),我們可以使用我們先前定義的 Actor 小雞喂食器來(lái)實(shí)現(xiàn)上述例子:

actor ChickenFeeder {
let food = "worms"
var numberOfEatingChickens: Int = 0

func chickenStartsEating() {
numberOfEatingChickens += 1
}

func chickenStopsEating() {
numberOfEatingChickens -= 1
}
}

你會(huì)注意到的第一件事是,這個(gè)實(shí)例更簡(jiǎn)單,更容易閱讀。所有與同步訪問(wèn)有關(guān)的邏輯都被隱藏在Swift標(biāo)準(zhǔn)庫(kù)中的實(shí)現(xiàn)細(xì)節(jié)里。然而,最有趣的部分發(fā)生在我們?cè)噲D使用或讀取任何可變屬性和方法的時(shí)候:

圖片

Methods in Actors are isolated for synchronized access.

Actors 中的方法是隔離的,以便同步訪問(wèn)。

在訪問(wèn)可變屬性 numberOfEatingChickens 時(shí),也會(huì)發(fā)生同樣的情況:

圖片

Mutable properties can only be accessed from within the Actor.

可變的屬性只能從 Actor 內(nèi)部訪問(wèn)。

然而,我們被允許編寫(xiě)以下代碼:

let feeder = ChickenFeeder()
print(feeder.food)

我們的喂食器上的 food 屬性是不可變的,因此是線程安全的。沒(méi)有數(shù)據(jù)競(jìng)爭(zhēng)的風(fēng)險(xiǎn),因?yàn)樵谧x取過(guò)程中,它的值不能從另一個(gè)線程中改變。

然而,我們的其他方法和屬性會(huì)改變一個(gè)引用類(lèi)型的可變狀態(tài)。為了防止數(shù)據(jù)競(jìng)爭(zhēng),需要同步訪問(wèn),允許按順序訪問(wèn)。

使用 async/await 訪問(wèn)數(shù)據(jù)

使用 async/await 從 Actors 訪問(wèn)數(shù)據(jù)

在 Swift 中,我們可以通過(guò)使用 await 關(guān)鍵字來(lái)創(chuàng)建異步訪問(wèn):

let feeder = ChickenFeeder()
await feeder.chickenStartsEating()
print(await feeder.numberOfEatingChickens) // Prints: 1

防止不必要的暫停

在上面的例子中,我們正在訪問(wèn)我們 Actor 的兩個(gè)不同部分。首先,我們更新吃食的雞的數(shù)量,然后我們執(zhí)行另一個(gè)異步任務(wù),打印出吃食的雞的數(shù)量。每個(gè) await 都會(huì)導(dǎo)致你的代碼暫停,以等待訪問(wèn)。在這種情況下,有兩個(gè)暫停是有意義的,因?yàn)閮刹糠制鋵?shí)沒(méi)有什么共同點(diǎn)。然而,你需要考慮到可能有另一個(gè)線程在等待調(diào)用 chickenStartsEating,這可能會(huì)導(dǎo)致在我們打印出結(jié)果的時(shí)候有兩只吃食的雞。

為了更好地理解這個(gè)概念,讓我們來(lái)看看這樣的情況:你想把操作合并到一個(gè)方法中,以防止額外的暫停。例如,設(shè)想在我們的 actor 中有一個(gè)通知方法,通知觀察者有一只新的雞開(kāi)始吃東西:

extension ChickenFeeder {
func notifyObservers() {
NotificationCenter.default.post(name: NSNotification.Name("chicken.started.eating"), object: numberOfEatingChickens)
}
}

我們可以通過(guò)使用 await 兩次來(lái)使用此代碼:

let feeder = ChickenFeeder()
await feeder.chickenStartsEating()
await feeder.notifyObservers()

然而,這可能會(huì)導(dǎo)致兩個(gè)暫停點(diǎn),每個(gè) await 都有一個(gè)。相反,我們可以通過(guò)從 chickenStartsEating 中調(diào)用 notifyObservers 方法來(lái)優(yōu)化這段代碼:

func chickenStartsEating() {
numberOfEatingChickens += 1
notifyObservers()
}

由于我們已經(jīng)在 Actor 內(nèi)有了同步的訪問(wèn),我們不需要另一個(gè)等待。這些都是需要考慮的重要改進(jìn),因?yàn)樗鼈兛赡軙?huì)對(duì)性能產(chǎn)生影響。

非隔離(nonisolated)訪問(wèn)

Actor 內(nèi)的非隔離(nonisolated)訪問(wèn)。

了解 Actor 內(nèi)部的隔離概念很重要。上面的例子已經(jīng)展示了如何通過(guò)要求使用 await 從外部參與者實(shí)例同步訪問(wèn)。但是,如果您仔細(xì)觀察,您可能已經(jīng)注意到我們的 notifyObservers 方法不需要使用 await 來(lái)訪問(wèn)我們的可變屬性 numberOfEatingChickens。

當(dāng)訪問(wèn) Actor 中的隔離方法時(shí),你基本上可以訪問(wèn)任何其他需要同步訪問(wèn)的屬性或方法。因此,你基本上是在重復(fù)使用你給定的訪問(wèn),以獲得最大的收益。

然而,在有些情況下,你知道不需要有隔離的訪問(wèn)。actor 中的方法默認(rèn)是隔離的。下面的方法只訪問(wèn)我們的不可變的屬性 food,但仍然需要 await 訪問(wèn)它:

let feeder = ChickenFeeder()
await feeder.printWhatChickensAreEating()

這很奇怪,因?yàn)槲覀冎溃覀儾辉L問(wèn)任何需要同步訪問(wèn)的東西。SE-0313[6]的引入正是為了解決這個(gè)問(wèn)題。我們可以用 nonisolated 關(guān)鍵字標(biāo)記我們的方法,告訴 Swift編 譯器我們的方法沒(méi)有訪問(wèn)任何隔離數(shù)據(jù):

extension ChickenFeeder {
nonisolated func printWhatChickensAreEating() {
print("Chickens are eating \(food)")
}
}

let feeder = ChickenFeeder()
feeder.printWhatChickensAreEating()

注意,你也可以對(duì)計(jì)算的屬性使用 nonisolated 的關(guān)鍵字,這對(duì)實(shí)現(xiàn) CustomStringConvertible 等協(xié)議很有幫助:

extension ChickenFeeder: CustomStringConvertible {   
nonisolated var description: String {
"A chicken feeder feeding \(food)"
}
}

然而,在不可變的屬性上定義它們是不需要的,因?yàn)榫幾g器會(huì)告訴你:

圖片

Marking immutable properties nonisolated is redundant.

將不可變的屬性標(biāo)記為 nonisolated 是多余的。

為什么會(huì)出現(xiàn)數(shù)據(jù)競(jìng)爭(zhēng)

為什么在使用 Actors 時(shí)仍會(huì)出現(xiàn)數(shù)據(jù)競(jìng)爭(zhēng)?

當(dāng)在你的代碼中持續(xù)使用 Actors 時(shí),你肯定會(huì)降低遇到數(shù)據(jù)競(jìng)爭(zhēng)的風(fēng)險(xiǎn)。創(chuàng)建同步訪問(wèn)可以防止與數(shù)據(jù)競(jìng)爭(zhēng)有關(guān)的奇怪崩潰。然而,你顯然需要持續(xù)地使用它們來(lái)防止你的應(yīng)用程序中出現(xiàn)數(shù)據(jù)競(jìng)爭(zhēng)。

在你的代碼中仍然可能出現(xiàn)競(jìng)爭(zhēng)條件,但可能不再導(dǎo)致異常。認(rèn)識(shí)到這一點(diǎn)很重要,因?yàn)锳ctors 畢竟被宣揚(yáng)為可以解決一切問(wèn)題的工具。例如,想象一下兩個(gè)線程使用 await正確地訪問(wèn)我們的 Actor 的數(shù)據(jù):

queueOne.async {
await feeder.chickenStartsEating()
}
queueTwo.async {
print(await feeder.numberOfEatingChickens)
}

這里的競(jìng)爭(zhēng)條件定義為:“哪個(gè)線程將首先開(kāi)始隔離訪問(wèn)?”。所以基本上有兩種結(jié)果:

  • 隊(duì)列一在先,增加吃食的雞的數(shù)量。隊(duì)列二將打印:1
  • 隊(duì)列二在先,打印出吃食的雞的數(shù)量,該數(shù)量仍為:0

這里的不同之處在于我們?cè)谛薷臄?shù)據(jù)時(shí)不再訪問(wèn)數(shù)據(jù)。如果沒(méi)有同步訪問(wèn),在某些情況下這可能會(huì)導(dǎo)致無(wú)法預(yù)料的行為。

結(jié)論

Swift Actors 解決了用 Swift 編寫(xiě)的應(yīng)用程序中常見(jiàn)的數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題。可變數(shù)據(jù)是同步訪問(wèn)的,這確保了它是安全的。我們還沒(méi)有介紹 MainActor 實(shí)例,它本身就是一個(gè)主題。我將確保在以后的文章中介紹這一點(diǎn)。希望您能夠跟隨并知道如何在您的應(yīng)用程序中使用 Actor。

參考資料

[1]數(shù)據(jù)競(jìng)爭(zhēng): https://www.avanderlee.com/swift/thread-sanitizer-data-races/#what-are-data-races?。

[2]解決這些問(wèn)題: https://www.avanderlee.com/swift/thread-sanitizer-data-races/#using-the-thread-sanitizer-to-detect-data-races?。

[3]Actor Model: https://en.wikipedia.org/wiki/Actor_model?。

[4]SE-0306: https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md?。

[5]Concurrent vs. Serial DispatchQueue: https://www.avanderlee.com/swift/concurrent-serial-dispatchqueue/。

[6]SE-0313: https://github.com/apple/swift-evolution/blob/main/proposals/0313-actor-isolation-control.md?。

責(zé)任編輯:姜華 來(lái)源: Swift社區(qū)
相關(guān)推薦

2015-03-16 10:33:14

Swift指針

2015-01-21 16:25:29

Swift指針

2022-10-08 08:31:09

Linuxsudo

2020-07-22 09:40:30

IT數(shù)據(jù)數(shù)據(jù)驅(qū)動(dòng)

2016-07-05 09:38:08

2013-04-15 09:48:40

AndroidAVD錯(cuò)誤處理方法

2022-12-06 23:43:53

iOSCreateML應(yīng)用

2018-07-23 14:51:22

2025-01-20 07:00:00

2023-05-09 15:42:54

數(shù)據(jù)中心漏水

2015-10-13 10:00:58

Swift隨機(jī)數(shù)使用總結(jié)

2018-07-30 08:20:39

編程語(yǔ)言Python集合

2011-03-16 10:29:17

2015-08-27 09:46:09

swiftAFNetworkin

2014-07-02 09:47:06

SwiftCocoaPods

2025-03-07 11:41:57

2017-05-25 11:49:30

Android網(wǎng)絡(luò)請(qǐng)求OkHttp

2020-12-15 13:44:32

人工智能AI

2013-10-12 08:30:17

2023-02-03 17:58:54

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲精品自拍视频 | 国产精品美女久久久久aⅴ国产馆 | 高清一区二区三区 | 国产中文字幕在线观看 | 在线观看黄色电影 | 日韩精品一区在线 | 亚洲美女网站 | 欧美日韩精品一区二区三区蜜桃 | 亚洲黄色在线免费观看 | 久草在线青青草 | caoporn免费在线视频 | 午夜网站视频 | 亚洲一区在线日韩在线深爱 | 久久国产视频网 | 天天宗合网 | 日韩a在线| 亚洲国产欧美在线 | 久久免费小视频 | 日本午夜一区二区三区 | 国产精品揄拍一区二区久久国内亚洲精 | 99精品久久久国产一区二区三 | 亚洲91av| a在线免费观看 | 亚洲不卡在线观看 | 精品国产视频在线观看 | h视频在线看 | 亚洲精品自在在线观看 | 一区二区三区在线观看视频 | 久久亚洲美女 | 一区久久 | 成人免费黄色 | 精精国产xxxx视频在线播放 | 国产精品成人一区二区 | 免费激情网站 | 成人精品一区二区三区中文字幕 | 二区在线观看 | 日韩中文字幕一区 | 亚洲人成人一区二区在线观看 | 亚洲一区二区电影在线观看 | 欧美日一区二区 | 激情亚洲 |