最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

iOS中多網(wǎng)絡(luò)請求的線程安全詳解

 更新時間:2017年10月22日 16:17:03   作者:Adam Sharp  
這篇文章主要給大家介紹了關(guān)于iOS中多網(wǎng)絡(luò)請求的線程安全的相關(guān)資料文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。

前言

在iOS 網(wǎng)絡(luò)編程有一種常見的場景是:我們需要并行處理二個請求并且在都成功后才能進(jìn)行下一步處理。下面是部分常見的處理方式,但是在使用過程中也很容易出錯:

  • DispatchGroup:通過 GCD 機(jī)制將多個請求放到一個組內(nèi),然后通過 DispatchGroup.wait() DispatchGroup.notify() 進(jìn)行成功后的處理。
  • OperationQueue:為每一個請求實例化一個 Operation 對象,然后將這些對象添加到 OperationQueue ,并且根據(jù)它們之間的依賴關(guān)系決定執(zhí)行順序。
  • 同步 DispatchQueue:通過同步隊列和 NSLock 機(jī)制避免數(shù)據(jù)競爭,實現(xiàn)異步多線程中同步安全訪問。
  • 第三方類庫:Futures/Promises 以及響應(yīng)式編程提供了更高層級的并發(fā)抽象。

在多年的實踐過程中,我意識到上面這些方法這些方法都存在一定的缺陷。另外,要想完全正確的使用這些類庫還是有些困難。

并發(fā)編程中的挑戰(zhàn)

使用并發(fā)的思維思考問題很困難:大多數(shù)時候,我們會按照讀故事的方式來閱讀代碼:從第一行到最后一行。如果代碼的邏輯不是線性的話,可能會給我們造成一定的理解難度。在單線程環(huán)境下,調(diào)試和跟蹤多個類和框架的程序執(zhí)行已經(jīng)是非常頭疼的一件事了,多線程環(huán)境下這種情況簡直不敢想象。

數(shù)據(jù)競爭問題:在多線程并發(fā)環(huán)境下,數(shù)據(jù)讀取操作是線程安全的而寫操作則是非線程安全。如果發(fā)生了多個線程同時對某個內(nèi)存進(jìn)行寫操作的話,則會發(fā)生數(shù)據(jù)競爭導(dǎo)致潛在數(shù)據(jù)錯誤。

理解多線程環(huán)境下的動態(tài)行為本身就不是一件容易的事,找出導(dǎo)致數(shù)據(jù)競爭的線程就更為麻煩。雖然我們可以通過互斥鎖機(jī)制解決數(shù)據(jù)競爭問題,但是對于可能修改的代碼來說互斥鎖機(jī)制的維護(hù)會是一件非常困難的事。

難以測試:并發(fā)環(huán)境下很多問題并不會在開發(fā)過程中顯現(xiàn)出來。雖然 Xcode 和 LLVM 提供了Thread Sanitizer 這類工具用于檢查這些問題,但是這些問題的調(diào)試和跟蹤依然存在很大的難度。因為并發(fā)環(huán)境下除了代碼本身的影響外,應(yīng)用也會受到系統(tǒng)的影響。

處理并發(fā)情形的簡單方法

考慮到并發(fā)編程的復(fù)雜性,我們應(yīng)該如何解決并行的多個請求?

最簡單的方式就是避免編寫并行代碼而是講多個請求線性的串聯(lián)在一起:

let session = URLSession.shared

session.dataTask(with: request1) { data, response, error in
 // check for errors
 // parse the response data

 session.dataTask(with: request2) { data, response error in
  // check for errors
  // parse the response data

  // if everything succeeded...
  callbackQueue.async {
   completionHandler(result1, result2)
  }
 }.resume()
}.resume()

為了保持代碼的簡潔,這里忽略了很多的細(xì)節(jié)處理,例如:錯誤處理以及請求取消操作。但是這樣將并無關(guān)聯(lián)的請求線性排序其實暗藏著一些問題。例如,如果服務(wù)端支持 HTTP/2 協(xié)議的話,我們就沒發(fā)利用 HTTP/2 協(xié)議中通過同一個鏈接處理多個請求的特性,而且線性處理也意味著我們沒有好好利用處理器的性能。

關(guān)于 URLSession 的錯誤認(rèn)知

為了避免可能的數(shù)據(jù)競爭和線程安全問題,我將上面的代碼改寫為了嵌套請求。也就是說如果將其改為并發(fā)請求的話:請求將不能進(jìn)行嵌套,兩個請求可能會對同一塊內(nèi)存進(jìn)行寫操作而數(shù)據(jù)競爭非常難以重現(xiàn)和調(diào)試。

解決改問題的一個可行辦法是通過鎖機(jī)制:在一段時間內(nèi)只允許一個線程對共享內(nèi)存進(jìn)行寫操作。鎖機(jī)制的執(zhí)行過程也非常簡單:請求鎖、執(zhí)行代碼、釋放鎖。當(dāng)然要想完全正確使用鎖機(jī)制還是有一些技巧的。

但是根據(jù) URLSession 的文檔描述,這里有一個并發(fā)請求的更簡單解決方案。

init(configuration: URLSessionConfiguration,
   delegate: URLSessionDelegate?,
   delegateQueue queue: OperationQueue?)

[…]

queue : An operation queue for scheduling the delegate calls and completion handlers. The queue should be a serial queue, in order to ensure the correct ordering of callbacks. If nil, the session creates a serial operation queue for performing all delegate method calls and completion handler calls.

這意味所有 URLSession 的實例對象包括 URLSession.shared 單例的回調(diào)并不會并發(fā)執(zhí)行,除非你明確的傳人了一個并發(fā)隊列給參數(shù) queue 。

URLSession 拓展并發(fā)支持

基于上面對 URLSession 的新認(rèn)知,下面我們對其進(jìn)行拓展讓它支持線程安全的并發(fā)請求(完成代碼地址)。

enum URLResult {
 case response(Data, URLResponse)
 case error(Error, Data?, URLResponse?)
}

extension URLSession {
 @discardableResult
 func get(_ url: URL, completionHandler: @escaping (URLResult) -> Void) -> URLSessionDataTask
}

// Example

let zen = URL(string: "https://api.github.com/zen")!
session.get(zen) { result in
 // process the result
}

首先,我們使用了一個簡單的 URLResult 枚舉來模擬我們可以在 URLSessionDataTask 回調(diào)中獲得的不同結(jié)果。該枚舉類型有利于我們簡化多個并發(fā)請求結(jié)果的處理。這里為了文章的簡潔并沒有貼出 URLSession.get(_:completionHandler:) 方法的完整實現(xiàn),該方法就是使用 GET 方法請求對應(yīng)的 URL 并自動執(zhí)行 resume() 最后將執(zhí)行結(jié)果封裝成 URLResult 對象。

@discardableResult
func get(_ left: URL, _ right: URL, completionHandler: @escaping (URLResult, URLResult) -> Void) -> (URLSessionDataTask, URLSessionDataTask) {
 
}

該段 API 代碼接受兩個 URL 參數(shù)并返回兩個 URLSessionDataTask 實例。下面代碼是函數(shù)實現(xiàn)的第一段:

 precondition(delegateQueue.maxConcurrentOperationCount == 1,
  "URLSession's delegateQueue must be configured with a maxConcurrentOperationCount of 1.")

因為在實例化 URLSession 對象時依舊可以傳入并發(fā)的 OperationQueue 對象,所以這里我們需要使用上面這段代碼將這種情況排除掉。

var results: (left: URLResult?, right: URLResult?) = (nil, nil)

func continuation() {
 guard case let (left?, right?) = results else { return }
 completionHandler(left, right)
}

將這段代碼繼續(xù)添加到實現(xiàn)中,其中定義了一個表示返回結(jié)果的元組變量 results 。另外,我們還在函數(shù)內(nèi)部定義了另一個工具函數(shù)用于檢查是否兩個請求都已經(jīng)完成結(jié)果處理。

let left = get(left) { result in
 results.left = result
 continuation()
}

let right = get(right) { result in
 results.right = result
 continuation()
}

return (left, right)

最后將這段代碼追加到實現(xiàn)中,其中我們分別對兩個 URL 進(jìn)行了請求并在請求都完成后一次返回了結(jié)果。值得注意的是這里我們通過兩次執(zhí)行 continuation() 來判斷請求是否全部完成:

  • 第一次執(zhí)行 continuation() 時因為其中一個請求并未完成結(jié)果為 nil 所以回調(diào)函數(shù)并不會執(zhí)行。
  • 第二次執(zhí)行的時候兩個請求全部完成,執(zhí)行回調(diào)處理。

接下來我們可以通過簡單的請求來測試下這段代碼:

extension URLResult {
 var string: String? {
  guard case let .response(data, _) = self,
  let string = String(data: data, encoding: .utf8)
  else { return nil }
  return string
 }
}

URLSession.shared.get(zen, zen) { left, right in
 guard case let (quote1?, quote2?) = (left.string, right.string)
 else { return }

 print(quote1, quote2, separator: "\n")
 // Approachable is better than simple.
 // Practicality beats purity.
}

并行悖論

我發(fā)現(xiàn)解決并行問題最簡單最優(yōu)雅的方法就是盡可能的少使用并發(fā)編程,而且我們的處理器非常適合執(zhí)行那些線性代碼。但是如果將大的代碼塊或任務(wù)拆分為多個并行執(zhí)行的小代碼塊和任務(wù)將會讓代碼變得更加易讀和易維護(hù)。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

作者:Adam Sharp,時間:2017/9/21

翻譯:BigNerdCoding, 如有錯誤歡迎指出。原文鏈接

相關(guān)文章

  • 適配iPhoneXS max和iPhoneX R的方法示例

    適配iPhoneXS max和iPhoneX R的方法示例

    這篇文章主要介紹了適配iPhoneXS max和iPhoneX R的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-10-10
  • iOS開發(fā)實現(xiàn)搜索框(UISearchController)

    iOS開發(fā)實現(xiàn)搜索框(UISearchController)

    這篇文章主要為大家詳細(xì)介紹了iOS開發(fā)實現(xiàn)搜索框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • iOS實現(xiàn)百度外賣頭像波浪的效果

    iOS實現(xiàn)百度外賣頭像波浪的效果

    對于現(xiàn)在很多人來說,叫外賣就成了不可或缺的習(xí)慣。某日瞬間發(fā)現(xiàn)百度外賣的APP波浪效果很是吸引人,相比較其他的外賣APP,顏值略高些.(淘寶也有波浪的效果),遂就思考如何實現(xiàn)這種"浪"的效果,下面來一起看看。
    2016-08-08
  • iOS ScrollView實現(xiàn)自動布局的方法(適用Swift 3.0 )

    iOS ScrollView實現(xiàn)自動布局的方法(適用Swift 3.0 )

    傳說中有一個美工ios開發(fā)者在遇到這個問題的時候特意跑到蘋果總部去咨詢?nèi)绾螌crollview進(jìn)行自動布局。當(dāng)然大家不用去了,下面這篇文章就來給大家介紹關(guān)于iOS ScrollView實現(xiàn)自動布局的方法,文中的語法同樣也適用Swift 3.0 ,需要的朋友可以參考下。
    2017-12-12
  • ios基于UICollectionView實現(xiàn)橫向瀑布流

    ios基于UICollectionView實現(xiàn)橫向瀑布流

    這篇文章主要為大家詳細(xì)介紹了ios基于UICollectionView實現(xiàn)橫向瀑布流,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • iOS10實現(xiàn)推送功能時的注意點(diǎn)和問題總結(jié)

    iOS10實現(xiàn)推送功能時的注意點(diǎn)和問題總結(jié)

    很多朋友都反饋,發(fā)現(xiàn)了iOS9升級到iOS10推送功能不正常的問題,所以這篇文章總結(jié)了一下要點(diǎn),親們可以根據(jù)以下步驟,逐步排查問題,也可以逐步實現(xiàn)iOS10的推送功能。下面來一起看看吧。
    2016-09-09
  • iOS實現(xiàn)應(yīng)用內(nèi)切換本地化語言的方法實例

    iOS實現(xiàn)應(yīng)用內(nèi)切換本地化語言的方法實例

    網(wǎng)絡(luò)上關(guān)于iOS國際化的文章很多,但基本上都是基于跟隨系統(tǒng)語言的國際化,而這篇文章主要給大家介紹了關(guān)于利用iOS實現(xiàn)應(yīng)用內(nèi)切換本地化語言的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考。
    2017-12-12
  • iOS實現(xiàn)微信朋友圈視頻截取功能

    iOS實現(xiàn)微信朋友圈視頻截取功能

    這篇文章主要介紹了iOS實現(xiàn)微信朋友圈視頻截取功能,微信使用非常普遍,功能也很強(qiáng)大,不知道大家對微信朋友圈視頻截取功能有沒有了解,下面腳本之家小編給大家?guī)碓斀饨榻B,感興趣的朋友一起看看吧
    2018-07-07
  • Objective-C中block循環(huán)引用問題詳解

    Objective-C中block循環(huán)引用問題詳解

    這篇文章主要給大家介紹了關(guān)于Objective-C中block循環(huán)引用問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Objective-C具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • 詳解iOS應(yīng)用程序的啟動過程

    詳解iOS應(yīng)用程序的啟動過程

    這篇文章主要介紹了iOS應(yīng)用程序的啟動過程,講述了從其執(zhí)行main函數(shù)開始到展示UIWindow的流程中的一些關(guān)鍵點(diǎn),需要的朋友可以參考下
    2016-03-03

最新評論

新兴县| 土默特左旗| 宽甸| 邢台市| 建昌县| 房产| 白朗县| 瓮安县| 成都市| 樟树市| 体育| 郁南县| 出国| 大余县| 若羌县| 麻江县| 柳林县| 四川省| 鄂托克旗| 九寨沟县| 龙海市| 兴仁县| 白沙| 密山市| 宣恩县| 额济纳旗| 永修县| 沂南县| 保靖县| 呼伦贝尔市| 辽阳市| 南京市| 宜都市| 金堂县| 永仁县| 忻州市| 南宫市| 栾城县| 大名县| 玛多县| 盘山县|