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


1.前言


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

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


2.正文


言歸正傳,我們繼續往下看,如果你對ios的中的GCD和DispatchQueue 使用很熟練的話,那么swift3.0的語法和使用應該就是輕車熟路,如果你還不是特別明白GCD是什么鬼?沒關系,這里先給大家來點山里的干貨:

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中執行這個方法,看下控制台打印的結果:


🚗

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

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


異步

看下輸出結果:


異步結果

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


通過上面的對比我們至少可以明白兩個件事:

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM