简介:
GCD(全称Grand CentralDispatch) 纯C语言
GCD优势:
1.为多核运算提供 2.自动管理线程的生命周期(创建线程、调度任务、销毁线程、),相比NSTread需要手动管理线程和生命周期更方便 3.只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
GCD中有两个核心概念: 任务和队列
1.任务: 执行什么操作 2.队列: 用来存放任务 (串行队列、并发队列) 3.将任务添加到队列,GCD会自动将队列中任务取出,放到对应线程中执行,任务取出遵循:先进先出,后进后出
提前说一下参数类型:
第一个参数: 队列的名称 第二个参数: 告诉系统需要创建一个并发队列还是串行队列 DISPATCH_QUEUE_SERIAL :串行 DISPATCH_QUEUE_CONCURRENT 并发
第一个参数: iOS8以前是优先级, iOS8以后是服务质量 iOS8以前 * - DISPATCH_QUEUE_PRIORITY_HIGH 高优先级 2 * - DISPATCH_QUEUE_PRIORITY_DEFAULT: 默认的优先级 0 * - DISPATCH_QUEUE_PRIORITY_LOW: 低优先级 -2 * - DISPATCH_QUEUE_PRIORITY_BACKGROUND: iOS8以后 * - QOS_CLASS_USER_INTERACTIVE 0x21 用户交互(用户迫切想执行任务) * - QOS_CLASS_USER_INITIATED 0x19 用户需要 * - QOS_CLASS_DEFAULT 0x15 默认 * - QOS_CLASS_UTILITY 0x11 工具(低优先级, 苹果推荐将耗时操作放到这种类型的队列中) * - QOS_CLASS_BACKGROUND 0x09 后台 * - QOS_CLASS_UNSPECIFIED 0x00 没有设置 第二个参数: 废物
执行任务: 同步、异步、栅栏
同步 dispatch_sync
: 只能在当前线程中执行任务,不具备开启新线程的能力
dispatch_sync(<#dispatch_queue_t queue#>, <#^(void)block#>)
异步 dispatch_async:可以在新的线程中执行任务,具备开启新线程的能力
dispatch_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
另外一个执行任务的方法:dispatch_barrier_async 栅栏
dispatch_barrier_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
(场景:在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行 注意:这个queue不能是全局的并发队列)
队列:串行与并行
使用dispatch_queue_create函数创建队列
dispatch_queue_t dispatch_queue_create( const char *Label,//队列名称 dispatch_queue_attr_t attr//队列的类型 );
并发队列: 多任务同时执行,开启多个线程
1.手动创建并发队列dispatch_queue_create
dispatch_queue_t queue = dispatch_queue_create(@"队列名称", DISPATCH_QUEUE_CONCURRENT);
2.GCD默认提供全局并发队列dispatch_get_global_queue
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
串行队列:任务一个个的执行
1.手动创建串行队列dispatch_queue_create
dispatch_queue_t queue = dispatch_queue_create(@"队列名称", NULL);
2.系统自带一种特殊串行队列
dispatch_queue_t queue = dispatch_get_main_queue();
同步、异步区别
同步: 在当前线程中执行任务,不开辟新的线程 异步:可以在新的线程中执行任务,可以开辟新线程
并发、串行区别
并发: 允许多个任务并发执行 串行: 一个任务执行完之后再执行下一个任务
同步函数 + 主队列(在子队列中进行)
dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_async(queue, ^{ //block会在子线程中执行 dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{ //block一定会在主线程执行 }); });
异步 + 主队列: 不会创建新的线程,并且任务都是在主线程中执行
dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(queue, ^{ //只要任务添加到主队列中,那么任务就一定会在主线程中执行 });
同步 + 并发 : 不会创建新的线程
//创建一个并发队列 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); //将任务添加到队列中 dispatch_sync(queue, ^{ NSLog(@"任务1"); }); dispatch_sync(queue, ^{ NSLog(@"任务2"); }); dispatch_sync(queue, ^{ NSLog(@"任务3"); }); NSLog(@"任务"); 输出: 任务1 任务2 任务3 任务
同步 + 串行 : 不会创建新的线程(如果调用同步函数,会等同步函数中得任务执行完之后再执行后面的的代码)
//创建一个串行队列 dispatch_queue_t queue = dispatch_queue_create("aaa", NULL); //将任务添加到队列中 dispatch_sync(queue, ^{ NSLog(@"任务1"); }); dispatch_sync(queue, ^{ NSLog(@"任务2"); }); dispatch_sync(queue, ^{ NSLog(@"任务3"); }); NSLog(@"任务");
异步 + 串行 : 会开启新的线程(只有一个) 如果调用一步函数,那么不用等函数中得任务执行完毕就可以执行后面的代码
//创建串行队列 dispatch_queue_t queue = dispatch_queue_create("aa", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ NSLog(@"任务1"); }); dispatch_async(queue, ^{ NSLog(@"任务2"); }); dispatch_async(queue, ^{ NSLog(@"任务3"); }); NSLog(@"任务");
异步 + 并发 : 会开启新的线程(如果任务多了会开启多个线程)
dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_async(queue, ^{ NSLog(@"任务1"); }); dispatch_async(queue, ^{ NSLog(@"任务2"); }); dispatch_async(queue, ^{ NSLog(@"任务3"); }); NSLog(@"任务");
线程间的通信
子线程回到主线程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //执行耗时的异步操作 NSLog(@"1"); }); dispatch_async(dispatch_get_main_queue(), ^{ //这里使用的是dispatch_async:不用等block执行完,就会调用下面的打印代码,如果替换为使用dispatch_sync同步函数:那么会等block执行更新UI完毕,才会执行最后一句打印代码 NSLog(@"2"); }); NSLog(@"刷新UI界面结束");
实例: 子线程下载图片,主线程刷新UI
//除主队列以外,随便写一个队列 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); //调用异步函数 dispatch_async(queue, ^{ //耗费时间 UIImage * image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://images.cnblogs.com/cnblogs_com/tupx/465992/o_torus_trianglestrip1.JPG"]]]; dispatch_sync(dispatch_get_main_queue(), ^{ //刷新主界面 self.imageView.image = image; }); });
延时执行
iOS常用延时执行,调用NSObject方法 (performSelector一旦执行号延时任务,不会卡住当前线程)
[self performSelector:@selector(run) withObject:nil afterDelay:1.0];
使用GCD函数
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{ NSLog(@"3秒后执行"); });
NSTimer
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(startTest) userInfo:nil repeats:NO]; - (void)startTest{ NSLog(@"---- begin-----"); }
[NSThread sleepForTimeInterval:3]; 延时执行不要使用sleepForTimeInterval (会卡住线程)
[NSThread sleepForTimeInterval:3];卡住线程3秒
一次性代码
dispatch_once可以保证函数内代码在程序运行期间值执行一次(不能当做懒加载使用)
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"一次性的代码"); });
快速迭代
第一个参数: 需要遍历几次 第二个参数: 决定第三个参数的block在哪个线程中执行 第三个参数: 回掉 dispatch_apply(10, 队列queue, ^(size_tindex){ //执行10次代码,index顺序不确定 });
//定义变量记录原始文件夹和目标文件夹的路径 NSString * sourcePatch = @"/原始文件"; NSString * destPath = @"/目标文件"; //取出原始文件中所有的文件 NSFileManager * manager = [NSFileManager defaultManager]; NSArray * files = [manager subpathsAtPath:sourcePatch]; dispatch_apply(files.count, dispatch_get_global_queue(0, 0), ^(size_t index) { NSString *fileName = files[index]; //生成原始文件的路径 NSString *sourceFilePath = [sourcePatch stringByAppendingPathComponent:fileName]; //生成目标文件的路径 NSString *destFilePath = [destPath stringByAppendingPathComponent:fileName]; //利用NSFileManager拷贝文件 [manager moveItemAtPath:sourceFilePath toPath:destFilePath error:nil]; });