概述:
1.NSThread 一般用做調試用,需要程序員管理生命周期,開發中較少使用。
2.GCD(iOS 4.0) 由系統管理,開發中使用的很多。
3.NSOperation(iOS 2.0) 基於GCD的OC封裝,開發中使用的較多。
- GCD(grand central dispatch)
核心概念:同步/異步,全局隊列/主隊列
全局隊列: { 同步:不開 異步:開N條 } 主隊列(奇葩): { 同步:卡死,不要用 異步:不開,因為他有主線程 } /** 開不開線程線程由任務是同步還是異步 同步:打死都不開 異步:除了主隊列,都開,開多少條,由我們的隊列的類型來決定 */
使用方式1:異步下載圖片,回到主線程更新UI
//一般下載,可能會下載多個 dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"downLoad----%@",[NSThread currentThread]); //去做耗時間的操作 //下載圖像...最后得到一個UIImage //去主線程更新UI dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"updateUI----%@",[NSThread currentThread]); }); });
使用方式2:線程安全-設置依賴關系(例如你異步下載20張圖片寫入一個數組中,為了保證效率所以數組為非線程安全的,如果解決多線程同時訪問的問題)
解決辦法是間接通過GCD的阻塞(dispatch_barrier_async)和同步
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //創建一個並發的隊列 /** 注意:如果你用了dispatch_barrier_async必須要用我們自己創建的並發隊列,而不能用全局隊列 */ _concurrentQueue = dispatch_queue_create("com.yun", DISPATCH_QUEUE_CONCURRENT); //循環去請求20個圖片資源 for (int i=0; i<20; ++i) { [self loadPic:i]; } } - (void)loadPic:(int)i{ //模擬網絡,通過URL去訪問Bundle里面的圖片 dispatch_async(_concurrentQueue, ^{ [NSThread sleepForTimeInterval:1.0]; NSString *fileName = [NSString stringWithFormat:@"%02d.jpg",i%10+1]; //1.URL NSURL *url =[[NSBundle mainBundle] URLForResource:fileName withExtension:nil]; //2.去Bundle里面加載我們的圖片二進制數據 NSData *data =[NSData dataWithContentsOfURL:url]; //3.將圖片的二進制數據,轉成UIImage對象 UIImage *image = [UIImage imageWithData:data]; //4.將我們的圖像添加到photoList數組中去 NSLog(@"%@----%d",[NSThread currentThread],i); dispatch_barrier_async(_concurrentQueue, ^{ [self.photoList addObject:image]; }); }); }
擴展:
如果想在dispatch_queue中所有的任務執行完成后在做某種操作,在串行隊列中,可以把該操作放到最后一個任務執行完成后繼續,但是在並行隊列中怎么做呢。這就有dispatch_group 成組操作。
dispatch_queue_t dispatchQueue = dispatch_queue_create("ted.queue.next", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t dispatchGroup = dispatch_group_create(); dispatch_group_async(dispatchGroup, dispatchQueue, ^(){ NSLog(@"dispatch-1"); }); dispatch_group_async(dispatchGroup, dispatchQueue, ^(){ NSLog(@"dspatch-2"); }); dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){ NSLog(@"end"); });
上面的 log1 和log2輸出順序不定,因為是在並行隊列上執行,當並行隊列全部執行完成后,最后到main隊列上執行一個操作,保證“end”是最后輸出。 另外,這里也可以不用創建自己的並行隊列,用全局的global,那個也是個並行隊列. dispatch_get_gloable_queue(0,0);
常用方式3:單例(dispatch_once)的創建和延遲執行(dispatch_after)
Tips:
在NSOperationQueue中,我們可以隨時取消已經設定要准備執行的任務(當然,已經開始的任務就無法阻止了),而GCD沒法停止已經加入queue的block(其實是有的,但需要許多復雜的代碼);
GCD原生並不支持取消操作。
dispatch_suspend函數也只能暫停開啟新的未執行的block,已經處於執行中的block是無法暫停的。
- NSOperation(抽象類不能直接使用,方式一:使用NSInvocationOperation 和 NSBlockOperation,常用方式二:自定義NSOperation即寫一個子類繼承自它需要重寫它的main方法就可以把子類的對象放入NSOperationQueue隊列中直接使用了)和NSOperationQueue
常見用法一:添加異步任務之間的依賴
/** 1.登錄 2.付費 3.下載 4.通知用戶 1.依賴的代碼,必須放在添加任務前 2.不要造成循環依賴,iOS7之前是直接崩,iOS8,不調度了 */ - (void)depencyDemo{ NSLog(@"%s",__FUNCTION__); NSBlockOperation *login = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@----login",[NSThread currentThread]); }]; NSBlockOperation *notifyUser = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@----notifyUser",[NSThread currentThread]); }]; NSBlockOperation *downLoad = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@----downLoad",[NSThread currentThread]); }]; NSBlockOperation *pay = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@----pay",[NSThread currentThread]); }]; NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; //GCD,同步,串行隊列 //NSOperation,addDependency就是表示我這個任務,要依賴於哪個任務執行完 [downLoad addDependency:pay]; [pay addDependency:login]; [notifyUser addDependency:downLoad]; //[login addDependency:downLoad];//??? //將任務添加到`隊列`中去 // [mainQueue addOperation:login]; // [mainQueue addOperation:notifyUser]; // [self.concurrentQueue addOperation:downLoad]; // [mainQueue addOperation:pay]; // 注意download是放在異步線程里的 [self.concurrentQueue addOperation:downLoad]; //YES,同步, 主隊列千萬不要和同步搞在一起 [mainQueue addOperations:@[login,notifyUser,pay] waitUntilFinished:NO]; }
打印結果如下:
[ViewController depencyDemo] <NSThread: 0x7fa88a707320>{number = 1, name = main}----login <NSThread: 0x7fa88a707320>{number = 1, name = main}----pay <NSThread: 0x7fa88a4bf850>{number = 2, name = (null)}----downLoad <NSThread: 0x7fa88a707320>{number = 1, name = main}----notifyUser
用法二:支持KVO可以觀察操作狀態(正在執行、是否結束、是否取消)
用法三:支持設置最大並發數(省電)而GCD不可以
用法四:異步耗時操作主隊列更新UI
//1.創建一個並發隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //2.創建任務,並將其添加到`並發隊列中` [queue addOperationWithBlock:^{ NSLog(@"login===>%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:2.0]; //去主線程更新UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [NSThread sleepForTimeInterval:3.0]; //更新UI的代碼 NSLog(@"%@===>",[NSThread currentThread]); }]; }];
有新的發現會繼續來完善....