为什么要用GCD-Swift2.x
當(dāng)今世界,多核已然普及。但是APP卻不見得很好的跟上了這個趨勢。APP?
想要利用好多核就必須可以保證任務(wù)能有效的分配。并行執(zhí)行可以讓APP同時執(zhí)行很多?
的任務(wù)。這個其實很難,但是有了GCD一切都變得簡單了很多。
你并不是一定要寫一個大并發(fā)的APP才需要用GCD。使用GCD可以讓你的APP更快的?
響應(yīng)用戶的操作,不用要等到你的UI或者服務(wù)等到執(zhí)行完成。一般來說你會把各種任務(wù)?
都分配給其他核心去執(zhí)行,而你的主線程(UI線程)可以隨時處理用戶的操作。?
GCD可以讓這些變得簡單。
線程
傳統(tǒng)的多任務(wù)分發(fā)方式是使用線程。在一個多核的設(shè)備上,每一個新的線程都可以被分配在一個CPU核心?
上,與其他的線程并行執(zhí)行。
但是使用線程也會遇到一個很大的問題。數(shù)據(jù)在線程之間的正確傳遞就是一個很大的難題了。線程之間的?
同步和互斥又會變得很詭異難以調(diào)試。而且,為了保證APP的高效快速運行,開辟多少線程也是一個需要思考的?
問題。因為,創(chuàng)建和銷毀線程也是有很大的資源消耗的。于是很多的系統(tǒng)都提供了一個叫做線程池?
的概念來解決這個問題。所有的線程創(chuàng)建后都放在這個池子里統(tǒng)一管理。
同步
當(dāng)你有多個線程在執(zhí)行的時候,你一般都會遇到一個問題Race Condition,實際的運算結(jié)果?
取決于那個線程先獲得共享數(shù)據(jù)。一個經(jīng)典的例子就是:銀行賬戶問題。
最后的結(jié)果取決于這兩個線程哪一個先執(zhí)行。在并行的條件下執(zhí)行的先后順序是不定的。但是執(zhí)行順序不同?
最后的balance值就是不同的。
- 1
- 1
對于這些問題,傳統(tǒng)的解決方法如下:?
* 信號量(semaphore)- 用來控制一組有限資源的消費。線程等待,知道資源可用。?
* 互斥(Mutex)- 一次只允許一個線程執(zhí)行。當(dāng)一個線程持有mutex的時候其他線程等待。?
* 條件變量(Condvar)- 線程等待直到某些條件為真。
隊列
GCD把線程的創(chuàng)建、回收以及線程的同步等進一步抽象為隊列(Queue)統(tǒng)一管理。
隊列,簡單而言,就是一個可以讓數(shù)據(jù)按照先進先出的順序執(zhí)行。一個APP可以創(chuàng)建多個隊列,并且多個?
隊列之間可以并行的處理各自的任務(wù),每個隊列內(nèi)部的任務(wù)順序執(zhí)行。
隊列比線程有很多的優(yōu)勢。第一,GCD庫屏蔽了線程管理的繁瑣部分。隊列會在需要的時候自動創(chuàng)建線程?
并且在不需要的時候回收。其次,GCD庫會根據(jù)系統(tǒng)的CPU核心數(shù)創(chuàng)建最佳數(shù)量的線程。最后,隊列只會?
在需要的時候創(chuàng)建線程,所以資源利用會得到優(yōu)化。
總之,隊列給你了你線程能給的,但是又不用考慮具體線程的操作。
GCD有三種隊列:?
* 順序隊列(Serial)- 每次執(zhí)行一個任務(wù),按照任務(wù)加入隊列的順序。一個任務(wù)執(zhí)行完成后執(zhí)行下一個。?
* 并發(fā)(Concurrent)- 按照任務(wù)加入隊列的順序開始,但是后面的任務(wù)不用等到前面的任務(wù)執(zhí)行完成就可以開始。?
* 主線程(Main)- 一個預(yù)先開啟的序列線程。這個隊列中包含一個NSRunLoop實例。你的APP總是在這個隊列中運行。
系統(tǒng)提供了幾種并發(fā)隊列。這些隊列有自己專屬的QoS(服務(wù)質(zhì)量種類)。這個服務(wù)質(zhì)量種類是用來表示你提交的?
任務(wù)的意圖是什么,這樣GCD可以有針對性的優(yōu)化。?
- QOS_CLASS_USER_INTERACTIVE?這個用戶交互(user interactive)表示任務(wù)需要立即執(zhí)行,以便APP?
? ? ? ? 給用戶一個良好的用戶體驗。一般用于更新UI、處理事件和小延遲的處理。在你的整個APP中,這以種類的任務(wù)應(yīng)該保持?
? ? ? ? 一個較低的總量。
-
QOS_CLASS_USER_INITIATED用戶初始化(user initiated)表示任務(wù)是在UI初始化的,并且可以?
異步執(zhí)行。這個一般用于處理用戶等待的需要理解給出運行結(jié)果,或者任務(wù)需要理解完成用戶交互的情況。 -
QOS_CLASS_UTILITY通用(utility)表示長期執(zhí)行的任務(wù),一般來說用戶可以見到任務(wù)執(zhí)行的比例。?
一般用來處理大的計算、I/O、網(wǎng)絡(luò)以及不簡單的數(shù)據(jù)提交等類似任務(wù)。這一種類做了電量優(yōu)化。 -
QOS_CLASS_BACKGROUND后臺運行(background)表示用戶并不知道任務(wù)在運行中。這個一般用于?
數(shù)據(jù)的提前加載、維護,以及其他無需用戶交互和任務(wù)完成時間無明顯限制的情況。
這里需要注意一點,蘋果的API也會用到這些全局分發(fā)的隊列。所以,在這些隊列里并不是只有你的任務(wù)在執(zhí)行。當(dāng)然,?
如前文所述你也可以創(chuàng)建你自己的隊列。你可以選擇4種全局隊列,主隊列,還有兩種自定義隊列可以選擇。
Closure
隊列的任務(wù)使用closure封裝。
你也可以使用方法加入隊列,不過這里只使用block。- 1
這里有一個closure的例子,讓你對closure有一個大概的印象。swift的closure就和Objective-C的block?
差不多。
上面的例子makeIncrementer返回一個closure,這個closure需要一個整型參數(shù)。
Hello dispatch world!
這里需要注意一點:Objective-C的block和Swift的closure。?
Swift的closure和Objective-C的block是兼容的。所以你完全可以把Swift的closure傳入Objective-C?
中需要block參數(shù)的方法中。Swift的closure和方法是同一類型的,所以你甚至可以把swift的方法名傳遞過去。
Closure和block的上下文處理語法基本一樣。唯一不同的是變量在closure中直接就是可變的。也就是說Objective-C?
中的__block關(guān)鍵字在Swift的closure中是默認行為。
調(diào)用dispatch_async(),GCD就會給隊列添加一個closure任務(wù),然后繼續(xù)代碼的執(zhí)行完全不會等待closure?
的結(jié)束。在我們的例子中,第一次dispatch_async是給QOS_CLASS_USER_INTERACTIVE的類型的全局?
隊列添加了一個任務(wù)。第二次是給dispatch_get_main_queue,也就是主線程添加了一個任務(wù)。
下面看一個自創(chuàng)隊列的例子:
@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. 創(chuàng)建一個名稱為concurrent.test.queue的并發(fā)隊列。?
2. 做一個1001次的循環(huán),每個循環(huán)給這個隊列添加一個closure。以上的寫法只是一個簡寫,其實是這樣的:
Barriers
很多人看到這里就會想,并發(fā)隊列在哪兒體現(xiàn)出來隊列的概念了呢?這分明就是一個把一堆closure扔進去分開執(zhí)行的堆或者Set(集合)?
而已嘛。
目前來看是的,但是當(dāng)你遇到barrier的時候?qū)α芯驼娴淖兂申犃辛恕D憧梢允褂胐ispatch_barrier_sync和?
dispatch_barrier_async兩個方法把closure加入隊列中。這個時候就很有意思了。扔進去的closure并?
不會立刻執(zhí)行,而是要等。等到在這個closure之前扔進隊列的全部closure都執(zhí)行完成之后才開始執(zhí)行。然后,?
在這個barrier的closure執(zhí)行完成之后,在它后面扔進隊列的closure才會被執(zhí)行。可以把barrier的closure認為?
是一個特殊的點,在這個點的從前到后都是順序執(zhí)行的。除此之外的點,還是并行執(zhí)行。
我們來看看具體的例子:
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最好是用在自己創(chuàng)建的并發(fā)隊列上。否則的話dispatch_barrier_async的效果就和dispatch_async一樣,而?
dispatch_barrier_sync就和dispatch_sync一樣。dispatch_barrier_sync要分配的隊列是當(dāng)前隊列?
的話會造成死鎖。
讀寫鎖
先看一個新聞累的單例的例子。這個例子的最初形態(tài)中不是一個線程安全的單例:
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. 實現(xiàn)一個單例。Swift的單例明顯要比Objective-C簡單了很多。是的,Swift的static屬性自動內(nèi)置了?
dispatch_once。所以,這么一樣就實現(xiàn)了Swift的單例。?
2.?init方法只能給類的內(nèi)部調(diào)用,但是不能給外部在調(diào)用,否則就不是單例了。
用戶可以調(diào)用news屬性來讀取全部的新聞,也可以通過調(diào)用方法addNews來添加新聞。?
當(dāng)時當(dāng)多個線程都可以訪問addNews方法的時候,那么news屬性讀出來的新聞列表就有很大的可能是錯的。?
我們現(xiàn)在使用barrier來確保這個功能可以正確的執(zhí)行。
解決辦法就是任何的線程要添加新聞,那么就必須通過barrier這一道關(guān)口。在添加新聞的時候只有一個closure執(zhí)行。?
其他的添加closure等待。
為了保證線程的安全,讀取新聞的操作也只能在concurrentQueue上執(zhí)行。但是這次我們是需要立即返回結(jié)果的?
沒法使用dispatch_async。這個時候使用dispatch_sync就是最好的選擇了。使用dispatch_sync?
可以等到方法執(zhí)行完畢,并返回我們需要的結(jié)果。dispatch sync可以知道dispatch barrier的進度如何,添加了?
幾條新聞。也可以讓closure執(zhí)行完成并返回。
但是使用dispatch sync需要很小心。如前所述,如果你給dispatch sync分配的隊列是當(dāng)前正在運行的隊列?
會造成死鎖。因為調(diào)用會等待這個closure結(jié)束,這個closure甚至可能都沒法開始。因為這個closure需要等到?
它前面運行的closure結(jié)束之后才能開始。
| 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上執(zhí)行讀取操作。?
2. 保存一份新聞的拷貝給newsCopy并返回。
現(xiàn)在這個新聞單例就是線程安全的了。下面我們看一下完整的代碼:
| 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 ????} } |
本文轉(zhuǎn)自張昺華-sky博客園博客,原文鏈接:http://www.cnblogs.com/sunshine-anycall/p/5126014.html,如需轉(zhuǎn)載請自行聯(lián)系原作者
總結(jié)
以上是生活随笔為你收集整理的为什么要用GCD-Swift2.x的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JAVA CXF、XFIRE、AXIS
- 下一篇: ProtoBuf协议