簡單介紹下GCD:
GCD:Grand Central Dispatch(偉大的中樞調度器),純C的函數,提供了很多非常強大的功能。
它是蘋果公司為多核的并行運算提出的解決方案,具有以下優點
1.GCD會自動利用更多的CPU內核(雙核、四核等)
2.自動管理線程的生命周期(創建任務、調度任務、銷毀線程)
3.程序員只需要告訴GCD想要執行什么任務,不需要編寫任何線程管理代碼,所以它大大解放了程序員
在GCD中有兩個核心的概念:任務和隊列。任務就是你要執行什么操作 隊列就是用來存放任務
使用GCD一般有兩個步驟:
1.定制任務,確定你要進行的操作
2.將任務添加到隊列中,GCD會自動將任務從隊列中取出(FIFO,先進先出原則),放到相應的線程中執行
GCD中有兩個主要執行任務的函數:
1.同步:(不能開啟新線程)
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block)
2.異步:(開啟新線程)
dispatch_async(dispatch_queue_t queue, dispatch_block_t block)
GCD中的隊列有可以分為兩大類型:
1.并發隊列(Concurrent Dispatch Queue):可以讓多個任務并發(同時)執行(自動開啟多個線程執行任務),并發功能只有在異步函數下才有效(dispatch_async)
2.串行隊列:任務一個接一個有序執行
GCD默認已經提供了全局的并發隊列,供整個應用使用,不需要手動創建:使用dispatch_get_global_queue函數獲得全局的并發隊列
串行隊列有兩種方式獲得:1.dispatch_queue_create創建 2.使用主隊列dispatch_get_main_queue(),主隊列是GCD自帶的一種特殊的串行隊列,放在主隊列的任務都會在主線程中執行
上代碼驗證下:
1.異步函數在并發隊列中執行
- (void)viewDidLoad { [super viewDidLoad]; // 1.獲得全局的并發隊列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 2.添加任務到隊列中執行任務 dispatch_async(queue, ^{ NSLog(@"下載1----%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"下載2----%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"下載3----%@",[NSThread currentThread]); });};
結果如圖:

可見程序在多線程中并發執行
2.異步函數在串行隊列中執行:
- (void)viewDidLoad { [super viewDidLoad]; [self asyncSerialQueue];}#pragma mark 異步+串行-(void)asyncSerialQueue{ // 1.創建串行隊列 dispatch_queue_t queue = dispatch_queue_create("gcd.queue", NULL); // 2.添加任務到隊列中執行任務 dispatch_async(queue, ^{ NSLog(@"下載1----%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"下載2----%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"下載3----%@",[NSThread currentThread]); });}
結果如圖:

可見程序開啟單個線程按順序執行
3.在同步函數中執行并發任務
- (void)viewDidLoad { [super viewDidLoad]; [self syncConcurrentQueue];}#pragma mark 同步+并發-(void)syncConcurrentQueue{ // 1.獲得全局的并發隊列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 2.添加任務到隊列中執行任務 dispatch_sync(queue, ^{ NSLog(@"下載1----%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"下載2----%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"下載3----%@",[NSThread currentThread]); });}
結果如圖:

在主線程中按順序執行,并發隊列失去并發功能
4.同步執行串行隊列:
- (void)viewDidLoad { [super viewDidLoad]; [self syncSerialQueue];}#pragma mark 同步+串行-(void)syncSerialQueue{ // 1.創建串行隊列 dispatch_queue_t queue = dispatch_queue_create("gcd.queue", NULL); // 2.添加任務到隊列中執行任務 dispatch_sync(queue, ^{ NSLog(@"下載1----%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"下載2----%@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"下載3----%@",[NSThread currentThread]); });}

結果同第三種情況,不會開啟新的線程,按順序執行
這里要特別注意,如果使用同步函數在主隊列中執行,會造成線程死鎖的情況
- (void)viewDidLoad{ [super viewDidLoad]; NSLog(@"1"); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"2"); }); NSLog(@"3");}
輸出結果為1
那么使用異步函數在主隊列中執行呢?
- (void)viewDidLoad{ [super viewDidLoad]; NSLog(@"1----%@",[NSThread currentThread]); dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"2----%@",[NSThread currentThread]); }); NSLog(@"3----%@",[NSThread currentThread]);}
結果為:

結果很特殊:不會開啟新的線程,而是主隊列中的任務會緩一緩,最后執行
綜上來看,各種隊列執行情況如下表:

關于GCD還有其他用法
1.延時執行:
- (void)viewDidLoad{ [super viewDidLoad]; // 通常的延時執行 dispatch_async(dispatch_get_main_queue(), ^{ [self performSelector:@selector(run) withObject:nil afterDelay:1.0]; }); // gcd函數執行 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)); dispatch_after(when, queue ^{ [self run]; }); }
2.保證某段代碼在執行過程中只執行一次(可用于實現單例模式):
- (void)viewDidLoad{ [super viewDidLoad]; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 執行一次的操作代碼 }); }
3.隊列組(通常用于耗時操作)
例如下載兩張圖片,要等兩張圖片都下載完成后,合并兩張圖,再更新主界面。由于下載圖片時間長短不可控,使用普通方法會很困難,這時使用隊列組可高效解決
- (void)viewDidLoad{ [super viewDidLoad]; // 創建一個組 dispatch_group_t group = dispatch_group_create(); // 下載圖片1 __block UIImage *image1 = nil; dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ image1 = [self downloadImageWithUrl:@"https://www.baidu.com/img/bdlogo.png"]; }); // 下載圖片2 __block UIImage *image2 = nil; dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ image2 = [self downloadImageWithUrl:@"http://img1.bdstatic.com/static/home/widget/search_box_home/logo/home_white_logo_0ddf152.png"]; }); // group任務都執行完畢,再執行其他操作 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 合并圖片 UIGraphicsBeginImageContext(CGSizeMake(300, 300)); [image1 drawInRect:CGRectMake(0, 0, 150, 300)]; [image2 drawInRect:CGRectMake(150, 0, 150, 300)]; UIGraphicsEndImageContext(); }); }-(UIImage *)downloadImageWithUrl:(NSString *)urlStr{ NSURL *url = [NSURL URLWithString:urlStr]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; return image;}
4.定時器
- (void)viewDidLoad{ [super viewDidLoad]; dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 15ull*NSEC_PER_SEC, 1ull*NSEC_PER_SEC); dispatch_source_set_event_handler(timer, ^{ NSLog(@"wake up"); dispatch_source_cancel(timer); }); dispatch_source_set_cancel_handler(timer, ^{ NSLog(@"cancel"); }); // 啟動 dispatch_resume(timer); }
此外,gcd還可監聽文件的讀和寫,信號的中斷等,有興趣的可以進一步鉆研
示例代碼可從Github下載https://github.com/VampireTMAC/GCDdemo
新聞熱點
疑難解答