iOS多執行緒程式設計技術之NSThread、Cocoa NSOperation、GCD

發表於2014-05-26

簡介
iOS有三種多執行緒程式設計的技術,分別是:
(一)NSThread
(二)Cocoa NSOperation
(三)GCD(全稱:Grand Central Dispatch)

這三種程式設計方式從上到下,抽象度層次是從低到高的,抽象度越高的使用越簡單,也是Apple最推薦使用的。

三種方式的優缺點介紹:
1)NSThread:
優點:NSThread 比其他兩個輕量級
缺點:需要自己管理執行緒的生命週期,執行緒同步。執行緒同步對資料的加鎖會有一定的系統開銷

NSThread實現的技術有下面三種:
~MA59HJ2_9MN7%]4Y%}FDGW
一般使用cocoa thread 技術。

Cocoa NSOperation
優點:不需要關心執行緒管理,資料同步的事情,可以把精力放在自己需要執行的操作上。
Cocoa operation 相關的類是 NSOperation ,NSOperationQueue。
NSOperation是個抽象類,使用它必須用它的子類,可以實現它或者使用它定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation。
建立NSOperation子類的物件,把物件新增到NSOperationQueue佇列裡執行。

GCD
Grand Central Dispatch (GCD)是Apple開發的一個多核程式設計的解決方法。在iOS4.0開始之後才能使用。GCD是一個替代諸如NSThread, NSOperationQueue, NSInvocationOperation等技術的很高效和強大的技術。現在的iOS系統都升級到7了,所以不用擔心該技術不能使用。

介紹完這三種多執行緒程式設計方式,本文將依次介紹這三種技術的使用。

(一)NSThread的使用
NSThread 有兩種直接建立方式:

第一個是例項方法,第二個是類方法

引數的意義:
selector :執行緒執行的方法,這個selector只能有一個引數,而且不能有返回值。
target  :selector訊息傳送的物件
argument:傳輸給target的唯一引數,也可以是nil

第一種方式會直接建立執行緒並且開始執行執行緒,第二種方式是先建立執行緒物件,然後再執行執行緒操作,在執行執行緒操作前可以設定執行緒的優先順序等執行緒資訊

不顯式建立執行緒的方法:
用NSObject的類方法  performSelectorInBackground:withObject: 建立一個執行緒:

下載圖片的例子:
新建singeView app
新建專案,並在xib檔案上放置一個imageView控制元件。按住control鍵拖到viewController.h檔案中建立imageView IBOutlet ViewController.m中實現:

執行緒間通訊
執行緒下載完圖片後怎麼通知主執行緒更新介面呢?

performSelectorOnMainThread是NSObject的方法,除了可以更新主執行緒的資料外,還可以更新其他執行緒的比如:

執行下載圖片:
VFE8W}]XP~E6K0RPJK10EIU

執行緒同步
我們演示一個經典的賣票的例子來講NSThread的執行緒同步:

如果沒有執行緒同步的lock,賣票數可能是-1.加上lock之後執行緒同步保證了資料的正確性。

上面例子我使用了兩種鎖,一種NSCondition ,一種是:NSLock。 NSCondition我已經註釋了。

執行緒的順序執行
他們都可以通過[ticketsCondition signal]; 傳送訊號的方式,在一個執行緒喚醒另外一個執行緒的等待。

比如:

wait是等待,我加了一個 執行緒3 去喚醒其他兩個執行緒鎖中的wait。

其他同步
我們可以使用指令 @synchronized 來簡化 NSLock的使用,這樣我們就不必顯示編寫建立NSLock,加鎖並解鎖相關程式碼。

還有其他的一些鎖物件,比如:迴圈鎖NSRecursiveLock,條件鎖NSConditionLock,分散式鎖NSDistributedLock等等,可以自己看官方文件學習

NSThread下載圖片的例子程式碼:http://download.csdn.net/detail/totogo2010/4591149

(二)Cocoa NSOperation的使用
使用 NSOperation的方式有兩種,
一種是用定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation。
另一種是繼承NSOperation

如果你也熟悉Java,NSOperation就和java.lang.Runnable介面很相似。和Java的Runnable一樣,NSOperation也是設計用來擴充套件的,只需繼承重寫NSOperation的一個方法main。相當與java 中Runnalbe的Run方法。然後把NSOperation子類的物件放入NSOperationQueue佇列中,該佇列就會啟動並開始處理它。

NSInvocationOperation例子:
這裡同樣,我們實現一個下載圖片的例子。新建一個Single View app,拖放一個ImageView控制元件到xib介面。
實現程式碼如下:

程式碼註釋:
1.viewDidLoad方法裡可以看到我們用NSInvocationOperation建了一個後臺執行緒,並且放到2.NSOperationQueue中。後臺執行緒執行downloadImage方法。
3.downloadImage 方法處理下載圖片的邏輯。下載完成後用performSelectorOnMainThread執行主執行緒updateUI方法。
updateUI 並把下載的圖片顯示到圖片控制元件中。

執行可以看到下載圖片顯示在介面上。
thjyjytktfb464dfgbdgw4

第二種方式繼承NSOperation 
在.m檔案中實現main方法,main方法編寫要執行的程式碼即可。

如何控制執行緒池中的執行緒數?
佇列裡可以加入很多個NSOperation, 可以把NSOperationQueue看作一個執行緒池,可往執行緒池中新增操作(NSOperation)到佇列中。執行緒池中的執行緒可看作消費者,從佇列中取走操作,並執行它。

通過下面的程式碼設定:

執行緒池中的執行緒數,也就是併發運算元。預設情況下是-1,-1表示沒有限制,這樣會同時執行佇列中的全部的操作。

(三)GCD的介紹和使用
介紹:
Grand Central Dispatch 簡稱(GCD)是蘋果公司開發的技術,以優化的應用程式支援多核心處理器和其他的對稱多處理系統的系統。這建立在任務並行執行的執行緒池模式的基礎上的。它首次釋出在Mac OS X 10.6 ,iOS 4及以上也可用。

設計:
GCD的工作原理是:讓程式平行排隊的特定任務,根據可用的處理資源,安排他們在任何可用的處理器核心上執行任務。

一個任務可以是一個函式(function)或者是一個block。 GCD的底層依然是用執行緒實現,不過這樣可以讓程式設計師不用關注實現的細節。

GCD中的FIFO佇列稱為dispatch queue,它可以保證先進來的任務先得到執行。

dispatch queue分為下面三種:
Serial
又稱為private dispatch queues,同時只執行一個任務。Serial queue通常用於同步訪問特定的資源或資料。當你建立多個Serial queue時,雖然它們各自是同步執行的,但Serial queue與Serial queue之間是併發執行的。

Concurrent
又稱為global dispatch queue,可以併發地執行多個任務,但是執行完成的順序是隨機的。

Main dispatch queue
它是全域性可用的serial queue,它是在應用程式主執行緒上執行任務的。

我們看看dispatch queue如何使用?

1、常用的方法dispatch_async
為了避免介面在處理耗時的操作時卡死,比如讀取網路資料,IO,資料庫讀寫等,我們會在另外一個執行緒中處理這些操作,然後通知主執行緒更新介面。

用GCD實現這個流程的操作比前面介紹的NSThread  NSOperation的方法都要簡單。程式碼框架結構如下:

如果這樣還不清晰的話,那我們還是用上兩篇部落格中的下載圖片為例子,程式碼如下:

執行顯示:
fdgrehrthjytjasf4535gfn

是不是程式碼比NSThread  NSOperation簡潔很多,而且GCD會自動根據任務在多核處理器上分配資源,優化程式。

系統給每一個應用程式提供了三個concurrent dispatch queues。這三個併發排程佇列是全域性的,它們只有優先順序的不同。因為是全域性的,我們不需要去建立。我們只需要通過使用函式dispath_get_global_queue去得到佇列,如下:

這裡也用到了系統預設就有一個序列佇列main_queue:

雖然dispatch queue是引用計數的物件,但是以上兩個都是全域性的佇列,不用retain或release。

2、dispatch_group_async的使用
dispatch_group_async可以實現監聽一組任務是否完成,完成後得到通知執行其他的操作。這個方法很有用,比如你執行三個下載任務,當三個任務都下載完成後你才通知介面說完成的了。下面是一段例子程式碼:

dispatch_group_async是非同步的方法,執行後可以看到列印結果:

每個一秒列印一個,當第三個任務執行後,upadteUi被列印。

3、dispatch_barrier_async的使用
dispatch_barrier_async是在前面的任務執行結束後它才執行,而且它後面的任務等它執行完成之後才會執行
例子程式碼如下:

列印結果:

請注意執行的時間,可以看到執行的順序如上所述。

4、dispatch_apply 
執行某個程式碼片段N次。

本篇使用的到的例子程式碼:http://download.csdn.net/detail/totogo2010/4596471
GCD還有很多其他用法,可以參考官方文件http://en.wikipedia.org/wiki/Grand_Central_Dispatch

相關文章