寫這一篇總結有些糾結。因為這是一個很大的題目,而我只是為了自己總結一下,沒想長篇大論寫教程。思來想去,還是寫一個備忘錄言簡意賅吧。
從apple給出的開發指引來看,apple官方是不推薦使用自訂線程的,而是推薦使用block、NSOpration這樣的方式進行非同步呼叫。因為內部的實現保證了更好的資源管理。並且給出了建立線程的代價參考。Mac OS和iOS不僅需要為線程分配堆棧空間,而且還需要分配核心空間。這樣看起來線程是非常消耗資源的。但是在某些情況下,又必須使用一些特殊的線程建立方式,例如程式結束之前的資料持久化操作,需要建立“可串連”線程(jointable)。這個時候就要用到Posix線程建立方式。
這裡是官方給出的線程消耗資源的參考表:
Item |
Approximate cost |
Notes |
Kernel data structures |
Approximately 1 KB |
This memory is used to store the thread data structures and attributes, much of which is allocated as wired memory and therefore cannot be paged to disk. |
Stack space |
512 KB (secondary threads) 8 MB (Mac OS X main thread) 1 MB (iOS main thread) |
The minimum allowed stack size for secondary threads is 16 KB and the stack size must be a multiple of 4 KB. The space for this memory is set aside in your process space at thread creation time, but the actual pages associated with that memory are not created until they are needed. |
Creation time |
Approximately 90 microseconds |
This value reflects the time between the initial call to create the thread and the time at which the thread’s entry point routine began executing. The figures were determined by analyzing the mean and median values generated during thread creation on an Intel-based iMac with a 2 GHz Core Duo processor and 1 GB of RAM running Mac OS X v10.5. |
線程建立時間,90毫秒。這個大於多數執行個體的建立時間。核心資料佔用大概1KB的空間,堆棧空間最小16KB,一般都是512KB for iOS/8MB for Mac.這個資料看起來就有點兒狠了。iphone程式建立4、5個現成,就要佔去2MB的資料,而iphone一般可用的堆棧空間大概只有100多MB。
不過堆棧空間大小在建立線程的時候是可以設定的:
NSThread - 可以調用[[[NSThread alloc] init…] setStackSize:int] 來設定。
POSIX - 設定pthread_attr_t,並使用pthread_attr_setstacksize方法來設定。
Multiprocessing Services - 通過MPCreateTask,傳遞stackSize參數來設定。
總結一下,有關線程的操作包括:
- NSThread
- NSOperation,通過NSOperationQueue來調度。
- 使用GCD
至於NSTimer、idle Notification,以及非同步呼叫,都屬於非同步呼叫的方法。我不認為是作為線程相關功能來總結了。
另外需要關注的是線程調度的問題。
首先是管理線程的結束。線上程結束時擷取通知,如果採用輪詢的方式非常浪費,我目前瞭解的至少有三種方法。
- 藉助semaphore. cocoa提供了dispatch semaphore通訊機制,可以實現wait和signal這樣的操作。主線程對某個semaphore進行等待。在每個線程結束的時候,對semaphore調用signal,解鎖主線程的等待。程式碼範例如下:
dispatch_semaphore_t sem = dispatch_semaphore_create(0);NSOperation * op1 = [[MyOperationObj alloc] initWithSema:sem];[queue addOperation:op1];dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);NSLog(@"the operation is ended");
- 藉助RunLoop。RunLoop是一個比較大的話題。詳細的細節可以參考這裡:http://www.cocoachina.com/iphonedev/sdk/2011/1111/3487.html. 至少比官方那繞口的語言好多了。基本上一看就明白了。程式碼範例:
while(op1.ended){[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];}//op1的main方法-(void)main{while(YES){.....}//when end[self performSelectorOnMainThread:@selector(notify) withObject:nil waitUntilEnd:NO];//performSelector可以立即觸發RunLoop事件處理。}
有關線程的資料傳遞,可以通過自訂連接埠的方式,或者藉助NSThread threadDictionary來傳遞。
然後所以下NSOperation。這是一個內建的線程執行方法。需要定義一個類,整合NSOperation,然後將這個類的執行個體放入NSOperationQueue中。
@interface MyOp:NSOperation@end@implementation @MyOp-(void)main{//body...}@end//main NSOperationQueue * queue = [NSOperationQueue new];MyOp * op = [MyOp new];[queue addOperation:op];[queue setMaxConcurrentOperationCount:2];//否則就都是同步按照放入順序執行[queue release];[op release];
最後再說一下block,應該就是GCD支援的方式。
定義一個block:
void(^bblock)(void)=^(void){//body;}
如果要在block中引用某個對象(或變數),這個對象(或變數)必須是聲明為static 或者用__block進行了修飾。
__block int blockLocal = 100;static int staticLocal = 100;
另外還有一個方法可以對block進行迭代,那就是block_apply.可以協助你將迴圈放入多核支援中。
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];initData();dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);__block int sum = 0;__block int *pArray = data;// iterations//dispatch_apply(Length, queue, ^(size_t i) {sum += pArray[i];});NSLog(@" >> sum: %d", sum);dispatch_release(queue);[pool drain];
定義好block以後,將它放入queue中進行執行
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_async(queue, bblock);
block中不能修改