亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > iOS > 正文

iOS中多網絡請求的線程安全詳解

2020-07-26 02:39:01
字體:
來源:轉載
供稿:網友

前言

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

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

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

并發編程中的挑戰

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

數據競爭問題:在多線程并發環境下,數據讀取操作是線程安全的而寫操作則是非線程安全。如果發生了多個線程同時對某個內存進行寫操作的話,則會發生數據競爭導致潛在數據錯誤。

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

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

處理并發情形的簡單方法

考慮到并發編程的復雜性,我們應該如何解決并行的多個請求?

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

let session = URLSession.sharedsession.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()

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

關于 URLSession 的錯誤認知

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

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

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

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 單例的回調并不會并發執行,除非你明確的傳人了一個并發隊列給參數 queue 。

URLSession 拓展并發支持

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

enum URLResult { case response(Data, URLResponse) case error(Error, Data?, URLResponse?)}extension URLSession { @discardableResult func get(_ url: URL, completionHandler: @escaping (URLResult) -> Void) -> URLSessionDataTask}// Examplelet zen = URL(string: "https://api.github.com/zen")!session.get(zen) { result in // process the result}

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

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

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

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

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

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

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

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

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

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

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

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.}

并行悖論

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

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。

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

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲欧美国产一区二区三区| 欧美风情在线观看| 成人在线视频网| 在线观看免费高清视频97| 欧美激情性做爰免费视频| 午夜精品一区二区三区在线视频| 日韩一区二区在线视频| 亚洲一区二区国产| 日韩电影在线观看永久视频免费网站| 久久精品国产欧美亚洲人人爽| 亚洲精品国产精品国自产观看浪潮| 青草青草久热精品视频在线观看| 国产成人精品优优av| 日韩av大片免费看| 欧美理论电影在线播放| 91av在线视频观看| 亚洲男人天堂网| 成人在线视频福利| 欧美理论电影在线观看| 久久精品青青大伊人av| 777777777亚洲妇女| 成人网欧美在线视频| 伊人伊成久久人综合网站| 久久亚洲精品国产亚洲老地址| 大伊人狠狠躁夜夜躁av一区| 黑人极品videos精品欧美裸| 欧美日本高清视频| 亚洲人成电影在线播放| 成人激情视频在线| 亚洲精品videossex少妇| 亚洲自拍小视频| 亚洲区一区二区| 久久久之久亚州精品露出| 欧美日韩国产精品专区| 久久精品电影网| 亚洲一区二区在线| 久久久久久久久久久成人| 亚洲精品成人av| 日韩大片在线观看视频| 日韩av男人的天堂| 欧美激情视频免费观看| 97在线免费观看| 日韩av免费网站| 亚洲成人精品视频在线观看| 欧美精品激情视频| 欧美另类在线观看| 色中色综合影院手机版在线观看| 国产精品丝袜视频| 亚洲精品小视频| 久久露脸国产精品| 欧美成人剧情片在线观看| 亚洲图片制服诱惑| 国产成人亚洲综合91精品| 日本伊人精品一区二区三区介绍| 欧美激情影音先锋| 成人网在线免费看| 欧美日韩aaaa| 午夜免费在线观看精品视频| 日韩精品视频免费专区在线播放| 久久久精品国产网站| 国产美女久久精品香蕉69| 国产不卡一区二区在线播放| 欧美另类高清videos| 日本午夜精品理论片a级appf发布| 亚洲欧美另类中文字幕| 成人h视频在线观看播放| 国产一区二区三区在线观看视频| 岛国av一区二区在线在线观看| 久久久电影免费观看完整版| 成人www视频在线观看| 日韩欧美国产中文字幕| 奇门遁甲1982国语版免费观看高清| 国产99久久精品一区二区 夜夜躁日日躁| 性色av一区二区三区| 26uuu另类亚洲欧美日本老年| 操人视频在线观看欧美| 欧美日韩ab片| 成人在线一区二区| 国产专区欧美专区| 伊人久久精品视频| 91精品国产91久久久久久| 欧美激情视频在线观看| 久久成人综合视频| 日韩av网站电影| 国产精品一区二区三区在线播放| 久久久久久网站| 92裸体在线视频网站| 日韩av电影在线免费播放| 亚洲欧洲偷拍精品| 欧美日韩午夜剧场| 亚洲一区二区三区乱码aⅴ蜜桃女| 国产精品毛片a∨一区二区三区|国| 国产精品视频免费观看www| 欧美精品久久久久久久免费观看| 精品中文字幕视频| 国产一区红桃视频| 亚洲天堂成人在线| 91精品国产91久久久久久| 国产午夜精品久久久| 国产精品福利网| 国产成人精品av在线| 岛国视频午夜一区免费在线观看| 欧美激情视频网| 在线亚洲午夜片av大片| 欧美激情久久久久久| 亚洲午夜精品视频| 亚洲free嫩bbb| 精品国产福利视频| 日韩欧美国产视频| 日韩美女在线看| 亚洲free性xxxx护士白浆| 精品呦交小u女在线| 国产日韩在线一区| 亚洲免费一在线| 日韩电影第一页| 久热99视频在线观看| 国产精品久久9| 国产精品青青在线观看爽香蕉| 日韩av片永久免费网站| 国产精品久久久久久久久久小说| 亚洲精品国产电影| 亚洲欧美另类自拍| 一区二区三区四区在线观看视频| 中文字幕日韩欧美精品在线观看| 久久人人爽人人| 国产精品久久久久久久美男| 亚洲精品久久7777777| 国产精品丝袜久久久久久不卡| www国产亚洲精品久久网站| 91产国在线观看动作片喷水| 色哟哟入口国产精品| 精品国产一区二区三区久久狼黑人| 欧美自拍视频在线观看| 国产欧美中文字幕| 在线播放国产一区中文字幕剧情欧美| 国产美女久久精品| 国产日韩综合一区二区性色av| 欧美人交a欧美精品| 欧美日韩高清在线观看| 国产精品久久久久久搜索| 久久久久久91| 欧美成人免费视频| 中文字幕久精品免费视频| 午夜精品一区二区三区视频免费看| 欧美最猛黑人xxxx黑人猛叫黄| 中文字幕亚洲欧美日韩2019| 日本老师69xxx| 国产va免费精品高清在线| 国产亚洲精品综合一区91| 九色精品免费永久在线| 国产精品流白浆视频| 中文字幕欧美亚洲| 欧美精品在线观看| 欧美专区在线观看| 日本久久久久亚洲中字幕| 日产精品久久久一区二区福利| 亚洲精品动漫久久久久| 一区二区福利视频| 日本伊人精品一区二区三区介绍| 亚洲精品91美女久久久久久久| 国产精品羞羞答答| 欧美一区二区三区图| 欧美日韩亚洲一区二区三区| 国产在线精品成人一区二区三区|