標籤:
IOS支援的多線程技術:
一、Thread:
1)顯式建立線程:NSThreed
2)隱式建立線程:NSObject
二、Cocoa operations:
NSOperation類是一個抽象類別,因為我們必須使用它的兩個子類。
1)NSInvocationOperation
2)NSBlockOperation
————————————————————————————
3)NSOperationQueue(繼承於NSObject)
三、Grand Central Dispatch (GCD):
1)GCD的建立
2)重複執行線程:dispatch_apply
3)操作(串列)隊列:dispatch_queue_create
4)GCD群組通知:dispatch_group_t
5)GCD實現計時器
以上三種多線程技術的對比:
一、Thread:
優點:量級較輕。
缺點:需要自己管理線程的生命週期,線程同步。線程同步對資料的加鎖會有一定的系統開銷。
二、Cocoa operations:
優點:不需要關心線程管理,資料同步的事情,可以把精力放在自己需要執行的操作上。
三、Grand Central Dispatch (GCD):
優點:GCD基於C語言的架構,可以充分利用多核,它能夠輕鬆在多核系統上高效運行並發代碼,是蘋果推薦使用的多線程技術。
以上三種線程技術,抽象層次從低到高,抽象度越高使用則越簡單,因此蘋果推薦我們使用GCD。
Other、線程間通訊:
在多線程中,所有修改有關於UI介面的東西,必須切換到主線程中修改,不能直接在多線程中修改,不然很容易會出現異常或修改不成功。本文會對三個線程技術說明如何切換至主線程修改UI,具體方法請往下看。
一、Thread
我們可以使用NSTherad或NSObject類去調用:
1)顯式建立線程:NSThread
建立NSThread有兩個辦法
1.1)建立之後需要使用start方法,才會執行方法:
NSThread *threadAlloc = [[NSThread alloc] initWithTarget:self selector:@selector(threadAlloc) object:nil];[threadAlloc start];
1.2)建立並馬上執行方法:
[NSThread detachNewThreadSelector:@selector(threadAlloc:) toTarget:self withObject:nil];
2)隱式建立線程:NSObject
我們也可以使用NSObject類的方法直接調用方法
[self performSelectorInBackground:@selector(threadAlloc) withObject:nil];
取消線程的方法:
實際上並沒有真正提供取消線程的API。蘋果提供了一個cancel的api,但它不能作用於取消線程,它只能改變線程的運行狀態。我們可以使用它來進行條件判斷。
- (void)threadCancel{ NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadCancelNow) object:nil]; [thread start];}- (void)threadCancelNow{ int a = 0; while (![[NSThread currentThread] isCancelled]) { NSLog(@"a - %d", a); a++; if (a == 5000) { NSLog(@"終止迴圈"); [[NSThread currentThread] cancel]; break; } }}
程式效果:迴圈輸出5000次,線程就會被終止。
NSThread線程間通訊-調用主線程修改UI:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
NSThread相關屬性及方法:
// 擷取/設定線程的名字@property (copy) NSString *name NS_AVAILABLE(10_5, 2_0);/** * 擷取當前線程的線程對象 * * 通過這個屬性可以查看當前線程是第幾條線程,主線程為1。 * 可以看到當前線程的序號及名字,主線程的序號為1,依次疊加。 */+ (NSThread *)currentThread;// 線程休眠(秒)+ (void)sleepForTimeInterval:(NSTimeInterval)ti;// 線程休眠,指定具體什麼時間休眠+ (void)sleepUntilDate:(NSDate *)date;// 退出線程 // 注意:這裡會把線程對象銷毀!銷毀後就不能再次啟動線程,否則程式會崩潰。+ (void)exit;
二、Cocoa operations
1)NSInvocationOperation
// 建立線程任務NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 線程ANSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operation1) object:nil];// 線程BNSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operation2) object:nil];// 把線程A/B添加至隊列[queue addOperations:@[operation, operation2] waitUntilFinished:YES];
必須使用addOperations:方法把線程添加至隊列,不然線程不會執行,隊列是並存執行。或者,你也可以使用addOperation:方法添加單個線程。
2)NSBlockOperation
舉個簡單的使用例子介紹,都寫了備忘,就不再說明了:
// 建立線程任務NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ [NSThread sleepForTimeInterval:2]; NSLog(@"one - %@", [NSThread currentThread]); }];;// 添加新的操作[blockOperation addExecutionBlock:^{ NSLog(@"two - %@", [NSThread currentThread]);}];// 添加新的操作[blockOperation addExecutionBlock:^{ NSLog(@"three - %@", [NSThread currentThread]);}];// 執行線程任務[blockOperation start];
例子效果:使用類方法blockOperationWithBlock:建立的塊代碼,是在主線程當中執行的,我們可以從列印出來的資訊看到。其它使用addExecutionBlock:添加的子線程為並發線程。
3)NSOperationQueue
這裡介紹一下NSOperation的依賴關係,依賴關係會影響線程的執行順序:
// 建立操作隊列NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 線程ANSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"op1"); [NSThread sleepForTimeInterval:2];}];// 線程BNSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"op2");}];// 線程CNSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"op3"); [NSThread sleepForTimeInterval:2];}];// 線程B依賴線程C,也就是等線程C執行完之後才會執行線程B[op2 addDependency:op3];// 線程C依賴線程A,同上,只不過依賴對象改成了線程A[op3 addDependency:op1];// 為隊列添加線程[queue addOperation:op1];[queue addOperation:op2];[queue addOperation:op3];
當你沒添加依賴時,隊列是並存執行的。
注意:依賴關係可以多重依賴,但不要建立循環相依性。
Cocoa operations線程間通訊-調用主線程修改UI:
// 建立線程對象(並發)NSBlockOperation *blockOperation = [[NSBlockOperation alloc] init];// 添加新的操作[blockOperation addExecutionBlock:^{ NSLog(@"two - %@", [NSThread currentThread]);}];// 添加新的操作[blockOperation addExecutionBlock:^{ NSLog(@"three - %@", [NSThread currentThread]); // 在主線程修改UI NSOperationQueue *queue = [NSOperationQueue mainQueue]; [queue addOperationWithBlock:^{ [self editUINow]; }];}];[blockOperation start];
NSOperation方法及屬性:
// 設定線程的最大並發數@property NSInteger maxConcurrentOperationCount;// 線程完成後調用的Block@property (copy) void (^completionBlock)(void);// 取消線程- (void)cancel;
只列舉上面那些,其它的方法就不全列出來了。
注意:在NSOperationQueue類中,我們可以使用cancelAllOperations方法取消所有的線程。這裡需要說明一下,不是執行cancelAllOperations方法時就會馬上取消,是等當前隊列執行完,下面的隊列不會再執行。
三、Grand Central Dispatch (GCD)
1)GCD的建立:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"線程 - %@", [NSThread currentThread]);});
GCD也可以建立同步的線程,只需要把async改成sync即可。
2)重複執行線程:dispatch_apply
以下代碼會執行4次:
dispatch_apply(4, DISPATCH_QUEUE_PRIORITY_DEFAULT, ^(size_t index) { // index則為執行的次數 0開始遞增 NSLog(@"one - %ld", index);});
index參數為執行的次數,從0開始遞增
3)操作隊列:dispatch_queue_create
// 建立隊列// 第一個參數是“字串標識”,第二個參數是可選的,可以是NULLdispatch_queue_t queue = dispatch_queue_create("com.garvey.post", NULL);// 傳入一個隊列並執行隊列(順序執行)dispatch_async(queue, ^{ NSLog(@"aaa"); [NSThread sleepForTimeInterval:2];});dispatch_async(queue, ^{ NSLog(@"bbb"); [NSThread sleepForTimeInterval:1];});dispatch_async(queue, ^{ NSLog(@"ccc");});
代碼效果:以上會先執行aaa-》bbb-》ccc,是一個串列隊列。
4)GCD群組通知:dispatch_group_t
GCD的進階用法,等所有線程都完成工作後,再作通知。
// 建立群組dispatch_group_t group = dispatch_group_create();// 線程Adispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"group1"); [NSThread sleepForTimeInterval:2];});// 線程Bdispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"group2");});// 待群組裡的線程都完成之後調用的通知dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"group success");});
群組裡的線程也是並行隊列。線程A和線程B都執行完之後,會調用通知列印group success。
5)GCD實現計時器
__block int time = 30;dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0);dispatch_source_set_event_handler(timer, ^{ time--; NSLog(@"%d", time); if (time == 0) { dispatch_source_cancel(timer); }});dispatch_resume(timer);
代碼效果:建立了一個計時器,計時器運行30秒,每過一秒會調用一次block,我們可以在block裡面寫代碼。因為dispatch_source_t預設是掛起狀態,因此我們使用時需要使用dispatch_resume方法先恢複,不然線程不會執行。
GCD線程間通訊-調用主線程修改UI:
有時候我們請求後台作資料處理,資料處理是非同步,資料處理完成後需要更新UI,這時候我們需要切換到主線程修改UI,例子如下:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"非同步資料處理 - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:2]; NSLog(@"資料處理完成"); // 調用主線程更新UI dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"更新UI - %@", [NSThread currentThread]); [self editUINow]; });});
因為是在主線程修改UI,所以我們最好是使用同步的GCD方法dispatch_sync。但這還不夠,我們還需要使用dispatch_get_main_queue()方法來獲得主線程,之後就是作UI的更新工作了。
GCD方法及屬性:
// 擷取主線程dispatch_get_main_queue()// 建立隊列:第一個參數是“字串標識”,第二個參數是可選的,可以是NULLdispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);// 建立非同步調度隊列void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);// 恢複隊列void dispatch_resume(dispatch_object_t object);// 暫停隊列void dispatch_suspend(dispatch_object_t object);
轉自GarveyCalvin
原文連結:http://www.cnblogs.com/GarveyCalvin/
ios 多線程開關,有關線程的一些用法和詳細講解,NSThread , NSOperation ,Grand Central Dispatch ( GCD )