iOS核心系統編程最佳實踐:線程 – iPhone手機開發技術文章 iPhone軟體開發教學課程

一、線程的創建:
操作對象(Operation objectis)可能創建線程更快,參閱:iOS核心系統編程最佳實踐:並發
1、線程創建需要的內存和時間消耗都比較大,因此建議你的入口點函數做相當數量的工作,或建立一個Run Loops允許進行經常性的工作。
2、Run Loops可以讓你使用最小的資源來創建長時間運行線程。因為run loop在沒有任何事件處理的時候會把它的線程置於休眠狀態,它消除瞭消耗CPU周期輪詢,並防止處理器本身進入休眠狀態並節省電源。
3、為瞭配置run loop,你所需要做的是啟動你的線程,獲取run loop的對象引用,設置你的事件處理程序,並告訴run loop運行。Cocoa和Carbon提供的基礎設施會自動為你的主線程配置相應的run loop。如果你打算創建長時間運行的輔助線程,那麼你必須為你的線程配置相應的run loop。
4、每一個線程都有其對應的RunLoop,但是默認非主線程的RunLoop是沒有運行的,需要為RunLoop添加至少一個事件源,然後去run它。一般情況下我們是沒有必要去啟用線程的RunLoop的,除非你在一個單獨的線程中需要長久的檢測某個事件。
5、RunLoop,就是一個循環,隻是這個循環裡加入很多特性。 首先循環體的開始需要檢測是否有需要處理的事件,如果有則去處理,如果沒有則進入睡眠以節省CPU時間。所以重點便是這個需要處理的事件,在RunLoop中,需要處理的事件分兩類,一種是輸入源,一種是定時器,定時器好理解就是那些需要定時執行的操作,輸入源分三類:performSelector源,基於端口(Mach port)的源,以及自定義的源。編程的時候可以添加自己的源。RunLoop還有一個觀察者Observer的概念,可以往RunLoop中加入自己的觀察者以便監控著RunLoop的運行過程,CFRunLoop.h中定義瞭所有觀察者的類型:
[html] 
enum CFRunLoopActivity { 
   kCFRunLoopEntry = (1 << 0), 
   kCFRunLoopBeforeTimers = (1 << 1), 
   kCFRunLoopBeforeSources = (1 << 2), 
   kCFRunLoopBeforeWaiting = (1 << 5), 
   kCFRunLoopAfterWaiting = (1 << 6), 
   kCFRunLoopExit = (1 << 7), 
   kCFRunLoopAllActivities = 0x0FFFFFFFU 
}; 
typedef enum CFRunLoopActivity CFRunLoopActivity; 
如果你使用過select系統調用寫過程序你便可以快速的理解runloop事件源的概念,本質上講事件源的機制和select一樣是一種多路復用IO的實現,在一個線程中我們需要做的事情並不單一,如需要處理定時鐘事件,需要處理用戶的觸控事件,需要接受網絡遠端發過來的數據,將這些需要做的事情統統註冊到事件源中,每一次循環的開始便去檢查這些事件源是否有需要處理的數據,有的話則去處理。拿具體的應用舉個例子,NSURLConnection網絡數據請求,默認是異步的方式,其實現原理就是創建之後將其作為事件源加入到當前的RunLoop,而等待網絡響應以及網絡數據接受的過程則在一個新創建的獨立的線程中完成,當這個線程處理到某個階段的時候比如得到對方的響應或者接受完瞭網絡數據之後便通知之前的線程去執行其相關的delegate方法。所以在Cocoa中經常看到<CODE>scheduleInRunLoop:forMode:</CODE>這樣的方法,這個便是將其加入到事件源中,當檢測到某個事件發生的時候,相關的delegate方法便被調用。對於CoreFoundation這一層而言,通常的模式是創建輸入源,然後將輸入源通過<CODE>CFRunLoopAddSource</CODE>函數加入到RunLoop中,相關事件發生後,相關的回調函數會被調用。如CFSocket的使用。另外RunLoop中還有一個運行模式的概念,每一個運行循環必然運行在某個模式下,而模式的存在是為瞭過濾事件源和觀察者的,隻有那些和當前RunLoop運行模式一致的事件源和觀察者才會被激活。 
二、線程的啟動:線程啟動之後,線程就進入三個狀態中的任何一個:運行(running)、就緒(ready)、阻塞(blocked)。如果一個線程當前沒有運行,那麼它不是處於阻塞,就是等待外部輸入,或者已經準備就緒等待分配CPU。線程持續在這三個狀態之間切換,直到它最終退出或者進入中斷狀態。
三、線程的執行:
1、線程同步:
NSLock:鎖提供瞭一次隻有一個線程可以執行代碼的有效保護形式。最普遍的一種鎖是互斥排他鎖,也就是我們通常所說的“mutex”。當一個線程試圖獲取一個當前已經被其他線程占據的互斥鎖的時候,它就會被阻塞直到其他線程釋放該互斥鎖。
NSCondition:條件確保在你的應用程序任務執行的適當順序。一個條件作為一個看門人,阻塞給定的線程,直到它代表的條件變為真。當發生這種情況的時候,條件釋放該線程並允許它繼續執行。
原子操作也是另外一種保護和同步訪問數據的方法。原子操作在以下情況的時候提供瞭替代鎖的輕量級的方法,其中你可以執行標量數據類型的數學或邏輯運算。原子操作使用特殊的硬件設施來保證變量的改變在其他線程可以訪問之前完成。
四、線程通信:Cocoa為iOS線程間通信提供2種方式,
一種是performSelector
[html] 
@interface NSObject (NSThreadPerformAdditions) 
 
– (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array; 
– (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; 
// equivalent to the first method with kCFRunLoopCommonModes 
 
– (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array NS_AVAILABLE(10_5, 2_0); 
– (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0); 
// equivalent to the first method with kCFRunLoopCommonModes 
    … 
@end 
另一種是NSMachPort(NSMachPort是個雞肋,線程間通信應該都通過performSelector來搞定)
 五、線程的退出
可以通過使用applicationShouldTerminate:的委托方法來延遲程序的中斷直到一段時間後或者完成取消。當延遲中斷的時候,你的程序需要等待直到任何周期線程已經完成它們的任務且調用瞭replyToApplicationShouldTerminate:方法。關於更多這些方法的信息,請查閱NSApplication Class Reference。
六、處理異常
1、Cocoa裡面,一個NSException對象是一個自包含對象,一旦它被引發瞭,那麼它可以從一個線程傳遞到另外一個線程。
2、在一些情況下,異常處理可能是自動創建的。比如,Objective-C中的@synchronized包含瞭一個隱式的異常處理。
3、如果在輔助線程裡面捕獲一個拋出的異常失敗,那麼你的主線程也同樣捕獲該異常失敗:它所屬的進程就會中斷。
4、如果你需要通知另一個線程(比如主線程)當前線程中的一個特殊情況,你應該捕捉異常,並簡單地將消息發送到其他線程告知發生瞭什麼事。
5、引發異常的線程可以繼續執行(如果可能的話),等待指示,或者幹脆退出。
 
 

發佈留言