IOS - 總結下swift使用GCD 多線程(二)GCD和DispatchQueue

1.前言


 iOS中處理多核併發的技術有兩種分別是:`Grand Central Dispatch`(如下簡稱`GCD`)和`NSOperationQueue`框架。iOS開發的老司機們在程序開發中處理多個任務同時執行的時候,必定都會使用到這兩個框架,並且GCD依靠它簡潔的語法和對block的運用一直很受你們的青睞。ios開發中你必定明白 這樣一條原則:「任何用於界面ui刷新和用戶交互的操做都要放在主線程來操做,任何耗時或者耗CPU的任務必須在異步線程去操做*」,----小白都會問爲什要這樣,老司機都說記住就好-------這裏就簡單解釋下:  ios

首先咱們來解釋第一句話:「任何用於界面ui刷新和用戶交互的操做都要放在主線程來操做」,要明白這句話只要明白下面幾個點:1.主線程是線程安全的--把全部ui刷新以及用戶的交互放在主線程操做會避免不少意外狀況的發生,保證在獲取服務端返回的數據時,ui界面能夠及時安全的刷新數據,給用戶帶來良好體驗 。2.ios中只有主線程才能夠馬上刷新ui界面,若是放在異步線程去操做都會形成線程阻塞和延遲的問題。---第二點「任何耗時或者耗CPU的任務必須在異步線程去操做」---若是你很好的明白了前半句,那麼這句話的意思就很好理解了,把耗時或者消耗cpu的操做放在異步線程,也就是爲了防止線程的阻塞延遲,防止主線程上的ui刷新和用戶操做的一系列動做出現卡頓,死鎖,延遲等問題。swift


2.正文


言歸正傳,咱們繼續往下看,若是你對ios的中的GCD和DispatchQueue 使用很熟練的話,那麼swift3.0的語法和使用應該就是輕車熟路,若是你還不是特別明白GCD是什麼鬼?不要緊,這裏先給你們來點山裏的乾貨:api

1. `dispatch queue`:一堆在主線程(或後臺線程)上同步(或異步)來執行的代碼,一旦被建立出來,操做系統就開始接手管理,在CPU上分配時間片來執行隊列內的代碼。開發者無法參與`queue`的管理。隊列採用`FIFO模式`(先進先出),意味着先加入的也會被先完成,這和超市排隊買單,隊伍前的老是最早買單出去,的道理是同樣同樣的。數組

2. `work item`:一段代碼塊,能夠在queue建立的時候添加,也能夠單首創建方便以後複用,你能夠把它當成將要在`queue`上運行的一個代碼塊。`work items`也遵循着`FIFO模式`,也能夠同步(或異步)執行,若是選擇同步的方式,運行中的程序直到代碼塊完成纔會繼續以後的工做,相對的,若是選擇異步,運行中的程序在觸發了代碼塊就馬上返回了。安全

3. `serial`(串行)vs`concurrent`(並行):`serial`將會執行完一個任務纔會開始下一個,`concurrent`觸發完一個就當即進入下一個,而無論它是否已完成。併發

接下來直奔主題:Swift3.0 中的GCD和DispatchQueue 使用。


1.'serial'(串行) vs 'concurrent'(並行)

   1.1 建立一個DispatchQueue的方法:


就是這麼簡單😊

  label:後面是一個標識,能夠隨便寫,通常建議寫成你的工程的dns的反序比較好。框架

   1.2 接下來咱們建立一個串行的queue 和 在主線程中執行的代碼對比下看看串行隊列和主線程的區別?


🚗

而後咱們在viewdidload中執行這個方法,看下控制檯打印的結果:異步


🚗

從結果中咱們能夠看到兩個方法是一個一個按順序來執行的,也就是說串行隊列和主線程同樣都是串行輸出的。換句話說也就是:主線程也是一個串行隊列。async

那異步(並行)隊列執行會是怎麼樣呢?函數


異步

看下輸出結果:


異步結果

此次咱們驚喜地發現和上次不一樣,而且主線程的函數和異步隊列的函數是交替執行 也就是說兩者同步的輸出,這是由於:異步隊列不會阻塞當前線程 而是會另開一個線程來執行當前的任務,而主線程上的任務也就不會被阻塞,因此兩者是同步輸出的。


經過上面的對比咱們至少能夠明白兩個件事:

1. 使用async主線程和後臺線程能夠並行執行任務

2. 使用sync則只能串行執行任務, 當前線程被卡住直到串行任務完成才繼續


2.GCD服務等級--Qos隊列


弄明白'serial'(串行) 和'concurrent'(並行)的關係,咱們繼續來看下GCD的qos隊列。

GCD服務等級(GCD QoS):肯定任務重要和優先級的屬性

QoS是個基於具體場景的枚舉類型,在初始隊列時,能夠提供合適的QoS參數來獲得相應的權限,若是沒有指定QoS,那麼初始方法會使用隊列提供的默認的QoS值

QoS等級(QoS classes),從前到後,優先級從高到低:

userInteractive

userInitiated

default

utility

background

unspecified

從上面的介紹 咱們猜測 確定是 ---等級越高的隊列越先被執行,同一等級下的隊列中 串行隊列確定是一個一個執行,異步隊列確定是分線程並行執行---下面咱們就來驗證下:

2.1 首先建立一個Qos隊列:


qos隊列

qos:須要傳一個DispatchQoS的枚舉類型

2.2 同個隊列同等級串行輸出對比


qosConcurrentQueues

看下輸出結果:


qosConcurrentQueues-log

仔細觀察 咱們會發現 結果並無像咱們猜測的那樣 同等級下的異步隊列 並行輸出,這是怎麼回事呢?由於Qos隊列默認是串行執行的,因此即便qos隊列中的方法是異步的 也會被順序串行執行。那麼怎樣才能夠並行執行呢?這就要用到Qos隊列的另一個屬性:attributes:.concurrent

具體寫法:


同等級並行

 

看下具體函數和運行結果:


同等級並行-log1

 


同等級並行-log2

經過輸出結果咱們發現 結果是並行輸出了,可是細心的你是否是發現告終果中的一絲絲不一樣,附上的兩張結果圖,是同一個函數運行屢次顯示的不一樣結果,爲何會不一樣呢?難道程序有問題,一個函數怎麼可能有兩個結果呢?是否是頓時一臉懵逼,一萬個草泥馬在心中奔騰😢😢😢😢😢😢😢😢😢😢😢😢😢😢😢😢----施主息怒,待山裏娃慢慢給你分析:

首先咱們要明白系統所謂的並行執行,並不全是咱們想象那的樣是固定的像log1中的同樣十分規矩的輸出,內部是會發生資源的傾斜或者順序的不肯定性的,繼續看下去後面的例子你定會完全明白。

咱們新建一個不一樣等級的Qos隊列來看下結果:


不一樣等級qos

輸出結果:


不一樣等級qos-log3

觀察結果 ,咱們會發現並非咱們以前猜想的 等級越高的隊列會快速先執行完,而是高等級和低等級的交叉進行輸出,仔細想下 其實這就是上面那個問題的完美詮釋,系統會使優先級更高的`queue1`比`queue2`更快被執行,雖然在`queue1`運行的時候`queue2`獲得一個運行的機會,系統仍是將資源傾斜給了被標記爲更重要的`queue1`,等`queue1`內的任務所有被執行完成,系統纔開始全心全意服務於`queue2` 。---看到這裏應該明白了吧。

你們能夠在思考個問題,在這些等級中 main queue主線程隊列是排在哪一個等級呢?下面咱們來看個例子:


main queue 對比

結果:


結果

從中咱們能夠清楚的得出結論:`main queue`默認就有一個很高的權限。


接下來咱們在來看下Qos隊列attributes的另一個屬性,initiallyInactive (不活躍的),咱們能夠建立一個qos的不活躍隊列,這個隊列的特色是 須要調用DispatchQueue類的`activate()`讓任務執行。看下具體代碼:


initiallyInactive 隊列

重點在下面,viewdidload中調用:


initiallyInactive 隊列 調用

看下輸出結果:


initiallyInactive 隊列-log

咱們發現隊列是串行輸出的,那麼怎樣建立一個並行的initiallyInactive 隊列呢?查看api咱們會發現Qos隊列的attributes接收的是一個數組,因此聰明的你確定知道怎麼辦吧😊


initiallyInactive並行 隊列  

3.延遲執行


當你的應用的某個流程中的某項任務延遲執行,GCD容許你執行某個方法來達到特定時間後運行你指定任務的目的。直接上代碼:


延遲加載

這裏解釋下:.now() 方法 是獲取當前時間。


4.DispatchWorkItem


DispatchWorkItem是一個代碼塊,它能夠被分到任何的隊列,包含的代碼能夠在後臺或主線程中被執行,簡單來講:它被用於替換咱們前面寫的代碼block來調用。使用起來也很簡單,看代碼:


DispatchWorkItem

5.主線程更新UI


最後給你們分享一個開篇說到的主線程刷新ui的例子,先看代碼:


主線程更新UI

使用的時候 記得在viewDidLoad 中調用setingIMage() 這個方法。

這裏主要想和你們介紹下swift中的懶加載和oc中的異同,我的總結了下你們能夠參閱:


懶加載

連接:http://www.jianshu.com/p/737233208d40

相關文章
相關標籤/搜索