苹果官方说明,GCD是异步执行任务的技术之一,通常将应用程序中记述的线程管理用的代码在系统级中实现。开发者只须要定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。因为线程管理是做为系统的一部分来实现的,所以能够统一管理,也能够执行任务。以下:数据库
//串形队列 第二个参数最好为 NULL 参考源码编程
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.gcd.mySerialQueue", NULL);
//并行队列 第二个参数最好为 DISPATCH_QUEUE_CONCURRENT数组
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.gcd.myCurrentQueue", DISPATCH_QUEUE_CONCURRENT);安全
dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 长时间处理 如:数据库访问 dispatch_async(dispatch_get_main_queue(), ^{ //主线程更新 }); });
其实只有第一行代码表示让处理在后台执行。也只有一行代码让处理在主线程中执行。多线程
在GCD以前,Cocoa框架也提供NSObject类来实现一样的效果:并发
//执行后台线程 [self performSelectorInBackground:@selector(doWork) withObject:nil];
- (void)doWork{ // 长时间处理 如:数据库访问 // ........................... //处理结束 ,主线城处理结果 [self performSelectorOnMainThread:@selector(doneWork) withObject:nil waitUntilDone:NO]; } - (void)doneWork{ //主线程执行 如用户界面更新 }
这个方法的确要比NSThread提供的简单,可是相比于GCD仍是繁琐了。在多线程编程中,因为CPU一次只能执行一个命令,GCD的优点就更加明显。app
------Dispatch Queue的种类框架
//获取主线程 属于serial queue dispatch_queue_t mainQueue = dispatch_get_main_queue(); //高优先级获取方法 属于concurrent queue dispatch_queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); //默认先级获取方法 属于concurrent queue dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //低优先级获取方法 属于concurrent queue dispatch_queue_t globalDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //后台优先级获取方法 属于concurrent queue dispatch_queue_t globalDispatchQueueBg = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);--
-----指定优先级 ,若是在多个serial dispatch queue中用此函数制定目标为某一个serial dispatch queue,那么本来能够并发执行的多个serial dispatch queue,在目标serial dispatch queue上只能同时执行一个处理。异步
//指定变动优先级 第一个参数若是是系统提供的如main dispatch queue 和 global dispatch queue 均不可指定 //第一个参数指定变动优先级 第二个参数指定与要使用的执行的相同优先级 dispatch_set_target_queue(globalDispatchQueueHigh, globalDispatchQueueLow);
-----Dispatch Groupasync
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t groupQueue = dispatch_group_create(); //这五个block并发执行 dispatch_group_async(groupQueue, queue, ^{NSLog(@"block0");}); dispatch_group_async(groupQueue, queue, ^{NSLog(@"block1");}); dispatch_group_async(groupQueue, queue, ^{NSLog(@"block2");}); dispatch_group_async(groupQueue, queue, ^{NSLog(@"block3");}); dispatch_group_async(groupQueue, queue, ^{NSLog(@"block4");}); //结束处理 必定是最后执行的 dispatch_group_notify(groupQueue, queue, ^{NSLog(@"done");});
dispatch_group中也可使用dispatch_wait函数等待所有处理执行结束
// DISPATCH_TIME_FOREVER 永久等待,group未结束就会一直等待 dispatch_group_wait(groupQueue, DISPATCH_TIME_FOREVER); //如: dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC); long result = dispatch_group_wait(groupQueue, time); if (result == 0) { //若是不为0,就说明虽然过了等待时间,可是某一个group还在处理执行中。 //属于dispatch group的所有处理执行结束 NSLog(@"属于dispatch group的所有处理执行结束"); }else{ //属于dispatch group的某一个处理还在执行中 NSLog(@"属于dispatch group的某一个处理还在执行中"); }
还有常常碰到的等待多个异步请求执行完毕方法以下:
dispatch_group_t serviceGroup = dispatch_group_create(); //开始第一个请求 //进入组 dispatch_group_enter(serviceGroup); [self.configService startWithCompletion:^(ConfigResponse *resul ts, NSError* error){ configError = error; //离开组 dispatch_group_leave(serviceGroup); }]; //开始第二个请求 //进入组 dispatch_group_enter(serviceGroup); [self.preferenceService startWithCompletion:^(PreferenceRespons e *results, NSError* error){ //离开组 preferenceError = error; dispatch_group_leave(serviceGroup); }]; //当小组里的任务都清空之后 通知主线程 dispatch_group_notify(serviceGroup,dispatch_get_main_queue(),^{ // Assess any errors NSError *overallError = nil; if (configError || preferenceError) { // Either make a new error or assign one of them to the overall error overallError = configError ?: preferenceError; } // Now call the final completion block completion(overallError); });
----dispatch_barrier_async在访问数据库或文件时,使用serial dispatch queue 可避免数据竞争。
dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.barrier", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{NSLog(@"block0_for_reading");}); dispatch_async(queue, ^{NSLog(@"block1_for_reading");}); dispatch_async(queue, ^{NSLog(@"block2_for_reading");}); dispatch_async(queue, ^{NSLog(@"block3_for_reading");}); //须要写入处理 -- 当前面执行完再执行dispatch_barrier中的处理,在执行后面的操做 dispatch_barrier_async(queue, ^{NSLog(@"blockBarrier_for_writing");}); dispatch_async(queue, ^{NSLog(@"block4_for_reading");}); dispatch_async(queue, ^{NSLog(@"block5_for_reading");}); dispatch_async(queue, ^{NSLog(@"block6_for_reading");}); dispatch_async(queue, ^{NSLog(@"block7_for_reading");}); dispatch_async(queue, ^{NSLog(@"block8_for_reading");});
----dispatch_apply 是dispatch_async和dispatch_group的关联API。
按指定的次数将指定的block追加到指定的dispatch_queue中,并等待所有执行结束。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(10, queue, ^(size_t index) { NSLog(@"%zu",index); }); NSLog(@"done");
有这样的功能,咱们就能够很安全的去循环数组进行元素处理(注:不是按顺序执行)。
NSArray *array = @[@"00",@"11",@"22",@"33",@"44",@"55",@"66",@"77",@"88",@"99",@"xx"]; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(array.count, queue, ^(size_t index) { NSLog(@"%zu-%@",index,array[index]); }); NSLog(@"done");
另外,因为dispatch_apply 和 diapatch_sync 函数相同。会等待处理执行结束,所以推荐在dispatch_async函数中非同步的执行dispatch_apply函数
NSArray *array = @[@"00",@"11",@"22",@"33",@"44",@"55",@"66",@"77",@"88",@"99",@"xx"]; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //在global dispatch queue中非同步执行 dispatch_async(queue, ^{ //等待dispatch_apply函数中所有处理执行结束 dispatch_apply(array.count, queue, ^(size_t index) { //并列处理包含在NSArray对象的所有对象 NSLog(@"%zu-%@",index,[array objectAtIndex:index]); }); //dispatch_apply函数所有处理执行结束 dispatch_async(dispatch_get_main_queue(), ^{ //主线程中更新UI NSLog(@"主线程中更新UI"); }); });
----dispatch_suspend 挂起中止执行, dispatch_resume恢复执行
----dispatch_semaphore 是持有计数的信号,此计数是变线程编程中计数类型信号。相似于过马路的手旗,能够经过举起手旗,不可经过放下手旗。dispatch_semaphore,计数为0等待,计数大于或等于1时,减去1而不等待。
//建立dispatch_semaphore 计数值初始化为1 dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC); long result = dispatch_semaphore_wait(semaphore, time); if (result == 0) {//返回为0的时候能够安全的执行能够进行排他控制的处理 /* 因为dispatch_semaphore的计数值达到大于等于1 或者在待机中的指定时间内 dispatch_semaphore的计数值达到大于等于1 因此dispatch_semaphore的计数值减去1. 可执行须要进行排他控制处理 */ }else{ /* 因为dispatch semaphore的计数值为0 所以在达到指定时间为止待机 */ }
实际使用:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //建立dispatch_semaphore 计数值初始化为1 //保证访问NSMutableArray类对象的线程同时只能有一个 dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); NSMutableArray *array = [[NSMutableArray alloc]init]; for (int i = 0 ; i < 10000; i ++) { dispatch_async(queue, ^{ /* 等待dispatch_smaphore 一直等待,直到dispatch_semaphore的计数值达到大于或等于1 */ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); /* 因为dispatch_semaphore的计数值达到大于等于1 因此将dispatch_semaphore的计数值减去1 dispatch_semaphore_wait函数执行返回 即此时的dispatch semaphore 恒为0 因为能够访问NSMutableArray类对象的线程只有1个 所以能够安全的进行更新 */ [array addObject:[NSNumber numberWithInt:i]]; /* 排他控制处理结束,因此经过dispatch_semaphore_signal函数将dispatch_samaphore的计数值加1. 若是有经过dispatch_semaphore_wait函数等待dispatch_semaphore的计数值增长的线程,就由最早等待的线程执行。 */ dispatch_semaphore_signal(semaphore); }); }
在没有serial dispatch queue和dispatch_barrier_async函数那么大粒度且一部分处理须要进行排他处理的状况下,diapatch_semaphore即可以发挥威力。
----Dispatch I/O
在读取较大文件时,若是将文件分红合适的大小并使用global dispatch queue并列读取的话,会比通常的读取快很多。如今的输入/输出硬件已经能够作到一次使用多个线程更快的并列读取了。能实现这个功能的只有Dispatch I/O 和 Dispatch Data,这个后面更新。