Swift并發(fā)系統(tǒng)并行運(yùn)行多個(gè)任務(wù)使用詳解
前言
Swift 內(nèi)置并發(fā)系統(tǒng)的好處之一是它可以更輕松地并行執(zhí)行多個(gè)異步任務(wù),這反過(guò)來(lái)又可以使我們顯著加快可以分解為單獨(dú)部分的操作。
在本文中,讓我們看一下幾種不同的方法,以及這些技術(shù)中的每一種何時(shí)特別有用。
從異步到并發(fā)
首先,假設(shè)我們正在開(kāi)發(fā)某種形式的購(gòu)物應(yīng)用程序來(lái)顯示各種產(chǎn)品,并且我們已經(jīng)實(shí)現(xiàn)了一個(gè)ProductLoader
允許我們使用一系列異步 API 加載不同產(chǎn)品集合的應(yīng)用程序,如下所示:
class ProductLoader { ... func loadFeatured() async throws -> [Product] { ... } func loadFavorites() async throws -> [Product] { ... } func loadLatest() async throws -> [Product] { ... } }
盡管大多數(shù)情況下上述每個(gè)方法都可能會(huì)被單獨(dú)調(diào)用,但假設(shè)在我們應(yīng)用程序的某些部分中,我們還希望形成一個(gè)Recommendations
包含這三個(gè)ProductLoader
方法的所有結(jié)果的組合模型:
extension Product { struct Recommendations { var featured: [Product] var favorites: [Product] var latest: [Product] } }
一種方法是使用await
關(guān)鍵字調(diào)用每個(gè)加載方法,然后使用這些調(diào)用的結(jié)果來(lái)創(chuàng)建我們Recommendations
模型的實(shí)例——如下所示:
extension ProductLoader { func loadRecommendations() async throws -> Product.Recommendations { let featured = try await loadFeatured() let favorites = try await loadFavorites() let latest = try await loadLatest() return Product.Recommendations( featured: featured, favorites: favorites, latest: latest ) } }
上面的實(shí)現(xiàn)確實(shí)有效——然而,即使我們的三個(gè)加載操作都是完全異步的,它們目前正在按順序執(zhí)行,一個(gè)接一個(gè)。因此,盡管我們的頂級(jí)loadRecommendations
方法相對(duì)于我們應(yīng)用程序的其他代碼正在并發(fā)執(zhí)行,但實(shí)際上它還沒(méi)有利用并發(fā)來(lái)執(zhí)行其內(nèi)部操作集。
由于我們的產(chǎn)品加載方法不以任何方式相互依賴,因此實(shí)際上沒(méi)有理由按順序執(zhí)行它們,所以讓我們看看如何讓它們完全同時(shí)執(zhí)行。
關(guān)于如何做到這一點(diǎn)的初步想法可能是將上述代碼簡(jiǎn)化為單個(gè)表達(dá)式,這將使我們能夠使用單個(gè)await
關(guān)鍵字來(lái)等待我們的每個(gè)操作完成:
extension ProductLoader { func loadRecommendations() async throws -> Product.Recommendations { try await Product.Recommendations( featured: loadFeatured(), favorites: loadFavorites(), latest: loadLatest() ) } }
然而,即使我們的代碼現(xiàn)在看起來(lái)是并發(fā)的,它實(shí)際上仍會(huì)像以前一樣完全按順序執(zhí)行。
相反,我們需要利用 Swift 的async let
綁定來(lái)告訴并發(fā)系統(tǒng)并行執(zhí)行我們的每個(gè)加載操作。使用該語(yǔ)法使我們能夠在后臺(tái)啟動(dòng)異步操作,而無(wú)需我們立即等待它完成。
await
如果我們?cè)趯?shí)際使用加載的數(shù)據(jù)時(shí)(即形成模型時(shí))將其與單個(gè)關(guān)鍵字組合Recommendations
,那么我們將獲得并行執(zhí)行加載操作的所有好處,而無(wú)需擔(dān)心狀態(tài)管理或數(shù)據(jù)競(jìng)爭(zhēng)之類(lèi)的事情:
extension ProductLoader { func loadRecommendations() async throws -> Product.Recommendations { async let featured = loadFeatured() async let favorites = loadFavorites() async let latest = loadLatest() return try await Product.Recommendations( featured: featured, favorites: favorites, latest: latest ) } }
很整齊!因此async let
,當(dāng)我們有一組已知的、有限的任務(wù)要執(zhí)行時(shí),它提供了一種同時(shí)運(yùn)行多個(gè)操作的內(nèi)置方法。但如果不是這樣呢?
任務(wù)組
現(xiàn)在假設(shè)我們正在開(kāi)發(fā)一個(gè)ImageLoader
可以讓我們通過(guò)網(wǎng)絡(luò)加載圖像的工具。要從給定的 加載單個(gè)圖像URL
,我們可以使用如下所示的方法:
class ImageLoader { ... func loadImage(from url: URL) async throws -> UIImage { ... } }
為了使一次加載一系列圖像變得簡(jiǎn)單,我們還創(chuàng)建了一個(gè)方便的 API,它接受一個(gè) URL 數(shù)組并異步返回一個(gè)圖像字典,該字典由下載圖像的 URL 鍵控:
extension ImageLoader { func loadImages(from urls: [URL]) async throws -> [URL: UIImage] { var images = [URL: UIImage]() for url in urls { images[url] = try await loadImage(from: url) } return images } }
現(xiàn)在讓我們說(shuō),就像我們ProductLoader
之前的工作一樣,我們想讓上面的loadImages
方法并發(fā)執(zhí)行,而不是按順序下載每個(gè)圖像(目前是這種情況,因?yàn)槲覀?code>await在調(diào)用時(shí)直接使用loadImage
我們的for
環(huán)形)。
但是,這次我們將無(wú)法使用async let
,因?yàn)槲覀冃枰獔?zhí)行的任務(wù)數(shù)量在編譯時(shí)是未知的。值得慶幸的是,Swift 并發(fā)工具箱中還有一個(gè)工具可以讓我們并行執(zhí)行動(dòng)態(tài)數(shù)量的任務(wù)——任務(wù)組。
要形成一個(gè)任務(wù)組,我們可以調(diào)用withTaskGroup
或withThrowingTaskGroup
,這取決于我們是否希望可以選擇在我們的任務(wù)中拋出錯(cuò)誤。在這種情況下,我們將選擇后者,因?yàn)槲覀兊牡讓?code>loadImage方法是用throws
關(guān)鍵字標(biāo)記的。
然后我們將遍歷每個(gè) URL,就像以前一樣,只是這次我們將每個(gè)圖像加載任務(wù)添加到我們的組中,而不是直接等待它完成。相反,我們將await
在添加每個(gè)任務(wù)之后單獨(dú)分組結(jié)果,這將允許我們的圖像加載操作完全并發(fā)執(zhí)行:
extension ImageLoader { func loadImages(from urls: [URL]) async throws -> [URL: UIImage] { try await withThrowingTaskGroup(of: (URL, UIImage).self) { group in for url in urls { group.addTask{ let image = try await self.loadImage(from: url) return (url, image) } } var images = [URL: UIImage]() for try await (url, image) in group { images[url] = image } return images } } }
要了解有關(guān)上述for try await
語(yǔ)法和一般異步序列的更多信息,請(qǐng)查看“異步序列、流和組合”。
就像使用 時(shí)一樣async let
,以我們的操作不會(huì)直接改變?nèi)魏螤顟B(tài)的方式編寫(xiě)并發(fā)代碼的一個(gè)巨大好處是,這樣做可以讓我們完全避免任何類(lèi)型的數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題,同時(shí)也不需要我們引入任何鎖定或序列化代碼混合在一起。
await
因此,在可能的情況下,讓我們的每個(gè)并發(fā)操作返回一個(gè)完全獨(dú)立的結(jié)果,然后依次返回這些結(jié)果以形成我們的最終數(shù)據(jù)集,這通常是一種很好的方法。
在以后的文章中,我們將更仔細(xì)地研究避免數(shù)據(jù)競(jìng)爭(zhēng)的其他方法(例如通過(guò)使用 Swift 的新actor
類(lèi)型)。
結(jié)論
重要的是要記住,僅僅因?yàn)榻o定的函數(shù)被標(biāo)記為async
并不一定意味著它同時(shí)執(zhí)行它的工作。相反,如果這是我們想要做的,我們必須故意讓我們的任務(wù)并行運(yùn)行,這只有在執(zhí)行一組可以獨(dú)立運(yùn)行的操作時(shí)才有意義。
以上就是Swift并發(fā)系統(tǒng)并行運(yùn)行多個(gè)任務(wù)使用詳解的詳細(xì)內(nèi)容,更多關(guān)于Swift并發(fā)運(yùn)行多個(gè)任務(wù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Swift 如何讓ScrollView滾動(dòng)到具體某個(gè)位置
這篇文章主要介紹了Swift 如何讓ScrollView滾動(dòng)到具體某個(gè)位置,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07Swift中使用可選類(lèi)型完美解決占位問(wèn)題
這篇文章主要介紹了Swift中使用可選類(lèi)型完美解決占位問(wèn)題,本文講解了為Dictionary增加objectsForKeys函數(shù)、Swift中更簡(jiǎn)便的方法、內(nèi)嵌可選類(lèi)型等內(nèi)容,需要的朋友可以參考下2015-05-05swift5.3 UIColor使用十六進(jìn)制顏色的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于swift5.3 UIColor使用十六進(jìn)制顏色的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10Swift 圖表使用Foudation庫(kù)中測(cè)量類(lèi)型詳解
這篇文章主要為大家介紹了Swift 圖表使用Foudation庫(kù)中測(cè)量類(lèi)型詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10Swift高階函數(shù)contains?allSatisfy?reversed?lexicographicallyPr
這篇文章主要為大家介紹了Swift高階函數(shù)contains?allSatisfy?reversed?lexicographicallyPrecedes用法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06RxSwift學(xué)習(xí)教程之類(lèi)型對(duì)象Subject詳解
這篇文章主要給大家介紹了關(guān)于RxSwift學(xué)習(xí)教程之類(lèi)型對(duì)象Subject的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起看看吧。2017-09-09