iOS開發實踐之多線程(GCD)
什麼是GCD:1、GCD是蘋果開發的一個多核編程的解決方案,和其它多線程技術方案相比,使用起來更加簡單和方便.
2、純C語言,提供了非常多強大的函數GCD的優勢:1、GCD是蘋果公司為多核的並行運算提出的解決方案
2、GCD會自動利用更多的CPU核心(比如雙核、四核)
3、GCD會自動管理線程的生命週期(建立線程、調度任務、銷毀線程)
4、程式員只需要告訴GCD想要執行什麼任務,不需要編寫任何線程管理代碼隊列和任務:
GCD中有2個核心概念
任務:執行什麼操作,用block來封裝任務。
隊列:用來存放任務。
將任務添加到隊列中GCD會自動將隊列中的任務取出,放到對應的線程中執行。
任務的取出遵循隊列的FIFO原則:先進先出,後進後出 。
隊列的類型
並發隊列:可以讓多個任務並發(同時)執行(自動開啟多個線程同時執行任務),並發功能只有在非同步(dispatch_async)函數下才有效。
1、獲得全域的並發隊列
//dispatch_queue_priority_t priority:隊列的優先順序 //unsigned long flags:此參數暫時無用,用0即可 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
全域並發隊列的優先順序
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 預設(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN// 後台
串列隊列:讓任務一個接著一個地執行(一個任務執行完畢後,再執行下一個任務)。
1、使用dispatch_queue_create函數建立串列隊列
//const char *label:隊列名稱//dispatch_queue_attr_t attr:隊列屬性,一般用NULL即可dispatch_queue_t queue = dispatch_queue_create("cn.XXX",NULL);
dispatch_release(queue); // 非ARC需要釋放手動建立的隊列
2、使用主隊列(跟主線程相關聯的隊列),主隊列是GCD內建的一種特殊的串列隊列
放在主隊列中的任務,都會放到主線程中執行
使用dispatch_get_main_queue()獲得主隊列
dispatch_queue_t queue = dispatch_get_main_queue();
執行任務:
1、用同步的方式執行任務
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
2、用非同步方式執行任務
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
同步和非同步區別:
1、同步:只能在當前線程中執行任務,不具備開啟新線程的能力。dispatch_sync
2、非同步:可以在新的線程中執行任務,具備開啟新線程的能力。 dispatch_async
隊列和執行任務方式組合分析:
ps 特別注意:使用sync函數往當前串列隊列中新增工作,會卡住當前的串列隊列
/** * 非同步全域並發 * async並發隊列(最常用) * 會建立線程,一般同時開多條 * 並發執行 **/-(void)asyncGlobalQueue{ //擷取全域並發隊列 //dispatch_queue_priority_t priority:隊列的優先順序 //unsigned long flags:此參數暫時無用,用0即可 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //將任務添加到全域隊列中去非同步執行 dispatch_async(queue, ^{ NSLog(@"------下載圖片1----%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"-----下載圖片2-----%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"----下載圖片3------%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"-----下載圖片4----%@",[NSThread currentThread]); }); }/** * 串列隊列(有時候會用) * 會建立線程,一般只開一條 * 串列執行,(一個任務執行完畢後再執行下一個任務) **/-(void)asyncSerialQueue{ //建立串列隊列 //const char *label:隊列名稱 //dispatch_queue_attr_t attr:隊列屬性,一般用NULL即可 dispatch_queue_t queue = dispatch_queue_create("cn.XXX",NULL); //將任務添加到串列隊列中 非同步執行 dispatch_async(queue,^{ NSLog(@"--------下載圖片1---------%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"--------下載圖片2---------%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"--------下載圖片3---------%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"--------下載圖片4---------%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"--------下載圖片5---------%@",[NSThread currentThread]); }); }/** * async --主隊列(很常用) **/-(void)asyncMainQueue{ //主隊列(添加到主隊列中的任務,都會自動放到主線程中去執行) dispatch_queue_t queue = dispatch_get_main_queue(); //新增工作到主隊列中非同步執行 dispatch_async(queue, ^{ NSLog(@"--------下載圖片1---------%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"--------下載圖片2---------%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"--------下載圖片3---------%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"--------下載圖片4---------%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"--------下載圖片5---------%@",[NSThread currentThread]); }); }/** * sync --主隊列(不能用,會卡死) **/-(void)syncMainQueue{ //主隊列(添加到主隊列中的任務,都會自動放到線程中去執行) dispatch_queue_t queue = dispatch_get_main_queue(); //將任務添加到主隊列中同步執行 dispatch_sync(queue, ^{ NSLog(@"--------下載圖片1---------%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"--------下載圖片2---------%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"--------下載圖片3---------%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"--------下載圖片4---------%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"--------下載圖片5---------%@",[NSThread currentThread]); });}/** * sync --並發隊列 * 不會建立線程 * 串列執行(一個任務執行完畢後再執行下一個任務) * 並發隊列失去了並發的功能 **/-(void)syncGlobalQueue{ //擷取全域的並發隊列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //將任務添加到全域並發隊列中同步執行 dispatch_sync(queue, ^{ NSLog(@"--------下載圖片1---------%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"--------下載圖片2---------%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"--------下載圖片3---------%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"--------下載圖片4---------%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"--------下載圖片5---------%@",[NSThread currentThread]); });}/** * sync --串列隊列 * 不會建立線程 * 串列執行(一個任務執行完畢後再執行下一個任務) **/-(void)syncSerialQueue{ //建立一個串列隊列 dispatch_queue_t queue = dispatch_queue_create("cn.xxx", NULL); //將任務添加到串列隊列中同步執行 dispatch_sync(queue, ^{ NSLog(@"--------下載圖片1---------%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"--------下載圖片2---------%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"--------下載圖片3---------%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"--------下載圖片4---------%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"--------下載圖片5---------%@",[NSThread currentThread]); });}
一次性執行:
//一次性執行 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //<#code to be executed once#> });
延時執行:
//延時執行(2秒)double delayInSeconds = 2;dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // <#code to be executed after a specified delay#>});
線程組:後台並發執行線程,等所有線程執行結束後再匯總執行結果
//1、擷取線程組dispatch_group_t group = dispatch_group_create(); //2、並存執行線程dispatch_group_async(group, queue, ^{ //並發執行線程一);ispatch_group_async(group, queue, ^{ //並發執行線程二}); //3、所有線程執行完成後調用dispatch_group_notify(group, queue, ^{ //匯總結果});
線程間通訊
從子線程回到主線程
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執行耗時的非同步作業...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主線程,執行UI重新整理操作
});
});
例子:子線程下載圖片,下載後更新介面的imageView.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ //子線程下載圖片 NSURL *url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/pic/item/37d3d539b6003af3290eaf5d362ac65c1038b652.jpg"]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; //回到主線程 dispatch_queue_t mainQueue = dispatch_get_main_queue(); dispatch_async(mainQueue, ^{ self.imageView.image = image; }); });}