为什么要用GCD-Swift2.x
當今世界,多核已然普及。但是APP卻不見得很好的跟上了這個趨勢。APP?
想要利用好多核就必須可以保證任務能有效的分配。并行執行可以讓APP同時執行很多?
的任務。這個其實很難,但是有了GCD一切都變得簡單了很多。
你并不是一定要寫一個大并發的APP才需要用GCD。使用GCD可以讓你的APP更快的?
響應用戶的操作,不用要等到你的UI或者服務等到執行完成。一般來說你會把各種任務?
都分配給其他核心去執行,而你的主線程(UI線程)可以隨時處理用戶的操作。?
GCD可以讓這些變得簡單。
線程
傳統的多任務分發方式是使用線程。在一個多核的設備上,每一個新的線程都可以被分配在一個CPU核心?
上,與其他的線程并行執行。
但是使用線程也會遇到一個很大的問題。數據在線程之間的正確傳遞就是一個很大的難題了。線程之間的?
同步和互斥又會變得很詭異難以調試。而且,為了保證APP的高效快速運行,開辟多少線程也是一個需要思考的?
問題。因為,創建和銷毀線程也是有很大的資源消耗的。于是很多的系統都提供了一個叫做線程池?
的概念來解決這個問題。所有的線程創建后都放在這個池子里統一管理。
同步
當你有多個線程在執行的時候,你一般都會遇到一個問題Race Condition,實際的運算結果?
取決于那個線程先獲得共享數據。一個經典的例子就是:銀行賬戶問題。
最后的結果取決于這兩個線程哪一個先執行。在并行的條件下執行的先后順序是不定的。但是執行順序不同?
最后的balance值就是不同的。
- 1
- 1
對于這些問題,傳統的解決方法如下:?
* 信號量(semaphore)- 用來控制一組有限資源的消費。線程等待,知道資源可用。?
* 互斥(Mutex)- 一次只允許一個線程執行。當一個線程持有mutex的時候其他線程等待。?
* 條件變量(Condvar)- 線程等待直到某些條件為真。
隊列
GCD把線程的創建、回收以及線程的同步等進一步抽象為隊列(Queue)統一管理。
隊列,簡單而言,就是一個可以讓數據按照先進先出的順序執行。一個APP可以創建多個隊列,并且多個?
隊列之間可以并行的處理各自的任務,每個隊列內部的任務順序執行。
隊列比線程有很多的優勢。第一,GCD庫屏蔽了線程管理的繁瑣部分。隊列會在需要的時候自動創建線程?
并且在不需要的時候回收。其次,GCD庫會根據系統的CPU核心數創建最佳數量的線程。最后,隊列只會?
在需要的時候創建線程,所以資源利用會得到優化。
總之,隊列給你了你線程能給的,但是又不用考慮具體線程的操作。
GCD有三種隊列:?
* 順序隊列(Serial)- 每次執行一個任務,按照任務加入隊列的順序。一個任務執行完成后執行下一個。?
* 并發(Concurrent)- 按照任務加入隊列的順序開始,但是后面的任務不用等到前面的任務執行完成就可以開始。?
* 主線程(Main)- 一個預先開啟的序列線程。這個隊列中包含一個NSRunLoop實例。你的APP總是在這個隊列中運行。
系統提供了幾種并發隊列。這些隊列有自己專屬的QoS(服務質量種類)。這個服務質量種類是用來表示你提交的?
任務的意圖是什么,這樣GCD可以有針對性的優化。?
- QOS_CLASS_USER_INTERACTIVE?這個用戶交互(user interactive)表示任務需要立即執行,以便APP?
? ? ? ? 給用戶一個良好的用戶體驗。一般用于更新UI、處理事件和小延遲的處理。在你的整個APP中,這以種類的任務應該保持?
? ? ? ? 一個較低的總量。
-
QOS_CLASS_USER_INITIATED用戶初始化(user initiated)表示任務是在UI初始化的,并且可以?
異步執行。這個一般用于處理用戶等待的需要理解給出運行結果,或者任務需要理解完成用戶交互的情況。 -
QOS_CLASS_UTILITY通用(utility)表示長期執行的任務,一般來說用戶可以見到任務執行的比例。?
一般用來處理大的計算、I/O、網絡以及不簡單的數據提交等類似任務。這一種類做了電量優化。 -
QOS_CLASS_BACKGROUND后臺運行(background)表示用戶并不知道任務在運行中。這個一般用于?
數據的提前加載、維護,以及其他無需用戶交互和任務完成時間無明顯限制的情況。
這里需要注意一點,蘋果的API也會用到這些全局分發的隊列。所以,在這些隊列里并不是只有你的任務在執行。當然,?
如前文所述你也可以創建你自己的隊列。你可以選擇4種全局隊列,主隊列,還有兩種自定義隊列可以選擇。
Closure
隊列的任務使用closure封裝。
你也可以使用方法加入隊列,不過這里只使用block。- 1
這里有一個closure的例子,讓你對closure有一個大概的印象。swift的closure就和Objective-C的block?
差不多。
上面的例子makeIncrementer返回一個closure,這個closure需要一個整型參數。
Hello dispatch world!
這里需要注意一點:Objective-C的block和Swift的closure。?
Swift的closure和Objective-C的block是兼容的。所以你完全可以把Swift的closure傳入Objective-C?
中需要block參數的方法中。Swift的closure和方法是同一類型的,所以你甚至可以把swift的方法名傳遞過去。
Closure和block的上下文處理語法基本一樣。唯一不同的是變量在closure中直接就是可變的。也就是說Objective-C?
中的__block關鍵字在Swift的closure中是默認行為。
調用dispatch_async(),GCD就會給隊列添加一個closure任務,然后繼續代碼的執行完全不會等待closure?
的結束。在我們的例子中,第一次dispatch_async是給QOS_CLASS_USER_INTERACTIVE的類型的全局?
隊列添加了一個任務。第二次是給dispatch_get_main_queue,也就是主線程添加了一個任務。
下面看一個自創隊列的例子:
@IBAction func concurrentAction(sender: AnyObject) {let concurrentQueue = dispatch_queue_create("concurrent.test.queue", DISPATCH_QUEUE_CONCURRENT) //1for i in 0...1000 { dispatch_async(concurrentQueue){ //2NSThread.sleepForTimeInterval(1) //3 print("print concurrent queue:- \(i)") //4}} }這里一步一步的介紹一下:?
1. 創建一個名稱為concurrent.test.queue的并發隊列。?
2. 做一個1001次的循環,每個循環給這個隊列添加一個closure。以上的寫法只是一個簡寫,其實是這樣的:
Barriers
很多人看到這里就會想,并發隊列在哪兒體現出來隊列的概念了呢?這分明就是一個把一堆closure扔進去分開執行的堆或者Set(集合)?
而已嘛。
目前來看是的,但是當你遇到barrier的時候對列就真的變成隊列了。你可以使用dispatch_barrier_sync和?
dispatch_barrier_async兩個方法把closure加入隊列中。這個時候就很有意思了。扔進去的closure并?
不會立刻執行,而是要等。等到在這個closure之前扔進隊列的全部closure都執行完成之后才開始執行。然后,?
在這個barrier的closure執行完成之后,在它后面扔進隊列的closure才會被執行??梢园裝arrier的closure認為?
是一個特殊的點,在這個點的從前到后都是順序執行的。除此之外的點,還是并行執行。
我們來看看具體的例子:
let concurrentQueue = dispatch_queue_create("concurrent.test.queue", DISPATCH_QUEUE_CONCURRENT)var count: Int = 0for _ in 0...100 {dispatch_async(concurrentQueue){NSThread.sleepForTimeInterval(1)print("print concurrent queue:- \(count++)")} }dispatch_barrier_async(concurrentQueue, {print("##ASYNC in barrier, concurrent queue - START")for _ in 1...10 {NSThread.sleepForTimeInterval(0.5)}print("##ASYNC in barrier, concurrent queue - END") })for _ in 0...100 {dispatch_async(concurrentQueue){NSThread.sleepForTimeInterval(1)print("print concurrent queue:- \(count++)")} }dispatch_barrier_sync(concurrentQueue, {print("##SYNC in barrier, concurrent queue - START")for _ in 1...10 {NSThread.sleepForTimeInterval(0.5)}print("##SYNC in barrier, concurrent queue - END") })for _ in 0...100 {dispatch_async(concurrentQueue){NSThread.sleepForTimeInterval(1)print("print concurrent queue:- \(count++)")} }注意barrier最好是用在自己創建的并發隊列上。否則的話dispatch_barrier_async的效果就和dispatch_async一樣,而?
dispatch_barrier_sync就和dispatch_sync一樣。dispatch_barrier_sync要分配的隊列是當前隊列?
的話會造成死鎖。
讀寫鎖
先看一個新聞累的單例的例子。這個例子的最初形態中不是一個線程安全的單例:
class NewsFeed {static let sharedInstance = NewsFeed() //1private init() {} //2private var _news: [String] = []var news: [String] {return _news}func addNews(n: String) {_news.append(n)} }?
有這么一個新聞的類。?
1. 實現一個單例。Swift的單例明顯要比Objective-C簡單了很多。是的,Swift的static屬性自動內置了?
dispatch_once。所以,這么一樣就實現了Swift的單例。?
2.?init方法只能給類的內部調用,但是不能給外部在調用,否則就不是單例了。
用戶可以調用news屬性來讀取全部的新聞,也可以通過調用方法addNews來添加新聞。?
當時當多個線程都可以訪問addNews方法的時候,那么news屬性讀出來的新聞列表就有很大的可能是錯的。?
我們現在使用barrier來確保這個功能可以正確的執行。
解決辦法就是任何的線程要添加新聞,那么就必須通過barrier這一道關口。在添加新聞的時候只有一個closure執行。?
其他的添加closure等待。
為了保證線程的安全,讀取新聞的操作也只能在concurrentQueue上執行。但是這次我們是需要立即返回結果的?
沒法使用dispatch_async。這個時候使用dispatch_sync就是最好的選擇了。使用dispatch_sync?
可以等到方法執行完畢,并返回我們需要的結果。dispatch sync可以知道dispatch barrier的進度如何,添加了?
幾條新聞。也可以讓closure執行完成并返回。
但是使用dispatch sync需要很小心。如前所述,如果你給dispatch sync分配的隊列是當前正在運行的隊列?
會造成死鎖。因為調用會等待這個closure結束,這個closure甚至可能都沒法開始。因為這個closure需要等到?
它前面運行的closure結束之后才能開始。
| 1 2 3 4 5 6 7 8 | private?var?_news: [String] = [] var?news: [String] { ????var?newsCopy: [String]! ????dispatch_sync(concurrentQueue){?//1 ????????newsCopy?=?self._news?//2 ????} ????return?newsCopy } |
下面逐一解釋:?
1. dispatch sync同步分配到concurrentQueue上執行讀取操作。?
2. 保存一份新聞的拷貝給newsCopy并返回。
現在這個新聞單例就是線程安全的了。下面我們看一下完整的代碼:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import?Foundation class?NewsFeed?{ ????static?let?sharedInstance?=?NewsFeed() ????private?let?concurrentQueue?=?dispatch_queue_create("newsfeed.queue.concurrent", ????????DISPATCH_QUEUE_CONCURRENT) ????private?init() {} ????private?var?_news: [String] = [] ????var?news: [String] { ????????var?newsCopy: [String]! ????????dispatch_sync(concurrentQueue){ ????????????newsCopy?=?self._news ????????} ????????return?newsCopy ????} ????func?addNews(n:?String) { ????????dispatch_barrier_async(concurrentQueue){ ????????????self._news.append(n) ????????????dispatch_async(dispatch_get_main_queue()){ ????????????????self.newsAddedNotification() ????????????} ????????} ????} ????func?newsAddedNotification() { ????????// post notification ????} } |
本文轉自張昺華-sky博客園博客,原文鏈接:http://www.cnblogs.com/sunshine-anycall/p/5126014.html,如需轉載請自行聯系原作者
總結
以上是生活随笔為你收集整理的为什么要用GCD-Swift2.x的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JAVA CXF、XFIRE、AXIS
- 下一篇: ProtoBuf协议