iOS|iOS GCD相关使用方法

1.GCD简介

Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。在 Mac OS X 10.6 雪豹中首次推出,也可在 iOS 4 及以上版本使用。
GCD优点
  • GCD可用于多核的并行运算
  • GCD会利用更多的CPU内核
  • GCD会自动管理线程的生命周期
2.GCD任务和队列 同步执行(sync):
  • 同步添加到指定的队列中,再添加的任务执行结束前,一直等待,在队列里的任务执行完成后在执行
  • 不具备开启线程的能力,在当前线程中执行。
异步执行:
  • 异步任务添加到队列中,不会等待,可以继续执行。
  • 可以下新的线程中执行任务。
队列: 用来存放任务的队列。队列是一种特殊的线性表,采用FIFO(先进先出)的原则。
  • 串行队列:每次只有一个任务会被执行,当一个任务执行完成后在执行下一个任务
  • 并发队列:可以让多个任务同时执行。可以开启多个线程,同时执行任务。
3.创建队列
  • 串行队列
/* *串行队列 *参数1:线程名称可以为空测试时可用 *参数2:串行队列 */ dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);

  • 并发队列
/* *并发队列 *参数1:线程名称可以为空测试时可用 *参数2:并发队列 */ dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);

  • 对于串行队列,GCD提供了一种特殊的串行队列主队列(Main Dispatch Queue)
    • 所有放到主队列中的任务,都会放到主线程中执行。
    • dispatch_get_main_queue()获取主队列。
dispatch_queue_t queue = dispatch_get_main_queue();

  • 并发队列,GCD默认提供了全局并发队列
/* *参数一 表示线程优先级 *参数二 0即可 */ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

4.GCD的基本使用
  • 同步执行 + 并发队列:
    不会开启新的线程,在当前线程执行,执行完一个任务后在执行下一个。
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 NSLog(@"syncConcurrent---begin"); dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^{ for (int i = 0; i<2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"1----%@",[NSThread currentThread]); } }); dispatch_sync(queue, ^{ for (int i = 0; i<2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"2----%@",[NSThread currentThread]); } }); dispatch_sync(queue, ^{ for (int i = 0; i<2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"3----%@",[NSThread currentThread]); } }); NSLog(@"syncConcurrent---end"); //打印信息 2018-12-10 15:21:27.866589+0800 GCD[11895:5222420] currentThread---{number = 1, name = main} 2018-12-10 15:21:27.866688+0800 GCD[11895:5222420] syncConcurrent---begin 2018-12-10 15:21:29.867784+0800 GCD[11895:5222420] 1----{number = 1, name = main} 2018-12-10 15:21:31.869445+0800 GCD[11895:5222420] 1----{number = 1, name = main} 2018-12-10 15:21:33.871222+0800 GCD[11895:5222420] 2----{number = 1, name = main} 2018-12-10 15:21:35.872828+0800 GCD[11895:5222420] 2----{number = 1, name = main} 2018-12-10 15:21:37.874443+0800 GCD[11895:5222420] 3----{number = 1, name = main} 2018-12-10 15:21:39.876046+0800 GCD[11895:5222420] 3----{number = 1, name = main} 2018-12-10 15:21:39.876239+0800 GCD[11895:5222420] syncConcurrent---end

  • 异步执行+并发队列:
    会开启新线程,所有任务同步执行。
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 NSLog(@"syncConcurrent---begin"); dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ for (int i = 0; i<2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"1----%@",[NSThread currentThread]); } }); dispatch_async(queue, ^{ for (int i = 0; i<2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"2----%@",[NSThread currentThread]); } }); dispatch_async(queue, ^{ for (int i = 0; i<2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"3----%@",[NSThread currentThread]); } }); NSLog(@"syncConcurrent---end"); //打印信息 2018-12-10 15:22:58.596939+0800 GCD[11898:5223017] currentThread---{number = 1, name = main} 2018-12-10 15:22:58.597029+0800 GCD[11898:5223017] syncConcurrent---begin 2018-12-10 15:22:58.597099+0800 GCD[11898:5223017] syncConcurrent---end 2018-12-10 15:23:00.603383+0800 GCD[11898:5223043] 2----{number = 4, name = (null)} 2018-12-10 15:23:00.603419+0800 GCD[11898:5223041] 1----{number = 3, name = (null)} 2018-12-10 15:23:00.603645+0800 GCD[11898:5223042] 3----{number = 5, name = (null)} 2018-12-10 15:23:02.606575+0800 GCD[11898:5223042] 3----{number = 5, name = (null)} 2018-12-10 15:23:02.608954+0800 GCD[11898:5223043] 2----{number = 4, name = (null)} 2018-12-10 15:23:02.609031+0800 GCD[11898:5223041] 1----{number = 3, name = (null)}

  • 同步执行+串行队列
    不会开启新的线程,在当前线程执行任务,任务是串行,在执行完成后一个任务在执行下一个任务。
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 NSLog(@"syncConcurrent---begin"); dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ for (int i = 0; i<2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"1----%@",[NSThread currentThread]); } }); dispatch_sync(queue, ^{ for (int i = 0; i<2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"2----%@",[NSThread currentThread]); } }); dispatch_sync(queue, ^{ for (int i = 0; i<2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"3----%@",[NSThread currentThread]); } }); NSLog(@"syncConcurrent---end"); //打印信息 2018-12-10 15:25:54.043186+0800 GCD[11901:5223884] currentThread---{number = 1, name = main} 2018-12-10 15:25:54.043275+0800 GCD[11901:5223884] syncConcurrent---begin 2018-12-10 15:25:56.044897+0800 GCD[11901:5223884] 1----{number = 1, name = main} 2018-12-10 15:25:58.046774+0800 GCD[11901:5223884] 1----{number = 1, name = main} 2018-12-10 15:26:00.048408+0800 GCD[11901:5223884] 2----{number = 1, name = main} 2018-12-10 15:26:02.050100+0800 GCD[11901:5223884] 2----{number = 1, name = main} 2018-12-10 15:26:04.051480+0800 GCD[11901:5223884] 3----{number = 1, name = main} 2018-12-10 15:26:06.054114+0800 GCD[11901:5223884] 3----{number = 1, name = main} 2018-12-10 15:26:06.054304+0800 GCD[11901:5223884] syncConcurrent---end

  • 异步执行 + 串行队列
    会开启新的线程,但是任务是串行执行,执行完成一个任务在执行下一个任务。
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 NSLog(@"syncConcurrent---begin"); dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ for (int i = 0; i<2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"1----%@",[NSThread currentThread]); } }); dispatch_async(queue, ^{ for (int i = 0; i<2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"2----%@",[NSThread currentThread]); } }); dispatch_async(queue, ^{ for (int i = 0; i<2; i++) { [NSThread sleepForTimeInterval:2]; NSLog(@"3----%@",[NSThread currentThread]); } }); NSLog(@"syncConcurrent---end"); //打印信息 2018-12-10 15:27:46.578929+0800 GCD[11904:5224555] currentThread---{number = 1, name = main} 2018-12-10 15:27:46.579018+0800 GCD[11904:5224555] syncConcurrent---begin 2018-12-10 15:27:46.579133+0800 GCD[11904:5224555] syncConcurrent---end 2018-12-10 15:27:48.585620+0800 GCD[11904:5224577] 1----{number = 3, name = (null)} 2018-12-10 15:27:50.590468+0800 GCD[11904:5224577] 1----{number = 3, name = (null)} 2018-12-10 15:27:52.596004+0800 GCD[11904:5224577] 2----{number = 3, name = (null)} 2018-12-10 15:27:54.601597+0800 GCD[11904:5224577] 2----{number = 3, name = (null)} 2018-12-10 15:27:56.607246+0800 GCD[11904:5224577] 3----{number = 3, name = (null)} 2018-12-10 15:27:58.610236+0800 GCD[11904:5224577] 3----{number = 3, name = (null)}

  • 同步执行 + 主队列(在主线程):
    出现死锁,相互等待卡主不执行。syncMain任务放到主线程中队列执行。同步执行会等待当前队列中的任务执行完成后才会执行
  • 异步执行 + 主队列 :
    在主线程中执行任务,执行完成一个任务后再执行下一个任务。
5.GCD信号量
  • 当我们在进行网络请求是,需要在一个请求执行完成后再去执行下一个请求是,因为网络请求是异步执行,需要用到信号量。当信号量为0的时候就会阻塞线程,大于0的时候就不会阻塞线程,通过改变信号量的值就可以控制线程。
  • dispatch_semaphore_create 创建信号量
  • dispatch_semaphore_signal
    1.返回值long类型,当返回值为0时,表示当前没有线程等待处理的信号,信号量增加1。
    2.当返回值不为0时,表示当前有一个会多个线程等待处理的信号量,并且该函数唤醒了一个等待的线程。
  • 【iOS|iOS GCD相关使用方法】dispatch_semaphore_wait
    1.等待信号,判断信号量是否大于0,如果大于0就减掉1个信号往下执行。
    2.如果等于0函数就会阻塞当前的线程。
//创建信号量 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); NSLog(@"开始网络请求"); [[SetNetWorking baseHttpManager] upLoadDataImage:image successBlock:^(id response) { //信号量+1NSLog(@"正在网络请求"); dispatch_semaphore_signal(semaphore); if (response) {}else{} } failedBlock:^(NSError * _Nonnull error) { dispatch_semaphore_signal(semaphore); [ToastView toastWithNSString:@"上传图片失败"]; }]; //信号量为0的时候回阻塞线程 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"网络请求完成"); //打印信息

6.GCD 队列组:dispatch_group
  • 创建队列组dispatch_group_t group = dispatch_group_create();
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"Request_1"); }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"Request_2"); }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"Request_3"); }); //队列组执行完后执行 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"任务均完成,刷新界面"); });

但是在进行网络请求是,请求都是异步执行,要实现在所有请求执行完成后再执行后面的任务需要用到dispatch_group_enter(group)dispatch_group_leave(group)
  • dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数+1
  • dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数-1
  • 当 group 中未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞,以及执行追加到dispatch_group_notify中的任务
dispatch_group_t group = dispatch_group_create(); dispatch_group_enter(group); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //请求1 [网络请求:{ 成功:dispatch_group_leave(group); 失败:dispatch_group_leave(group); }]; }); dispatch_group_enter(group); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [网络请求:{ 成功:dispatch_group_leave(group); 失败:dispatch_group_leave(group); }]; }); dispatch_group_enter(group); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [网络请求:{ 成功:dispatch_group_leave(group); 失败:dispatch_group_leave(group); }]; }); //队列组执行完后执行 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"任务均完成,刷新界面"); });

  • dispatch_group_wait会阻塞当前线程,当组中多有任务完成后再执行下面任务
GCD栅栏 dispatch_barrier_async在异步执行两组操作,需要实现在执行完一组后在执行一组是需要使用到栅栏
7.GCD定时器
__block NSInteger time = 10; //倒计时时间//创建全局队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //使用全局队列创建计时器 dispatch_source_t sourceTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); /* *参数一:传入计时器 *参数二:计时器开始时间 *参数三:计时器间隔时间 *参数四:代表精准度,0是最精确 */ dispatch_source_set_timer(sourceTimer, DISPATCH_TIME_NOW, 1.0*NSEC_PER_SEC, 0); //开始执行计时器任务 dispatch_source_set_event_handler(sourceTimer, ^{ if (time<=0) { //计时器关闭 dispatch_source_cancel(sourceTimer); NSLog(@"执行完成"); }else{ NSLog(@"%ld",time); } time --; }); //开始执行计时器任务 dispatch_resume(sourceTimer);

8.GCD一次性代码只执行一次。
  • 我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了 GCD 的dispatch_once 函数。使用
    dispatch_once 函数能保证某段代码在程序运行过程中只被执行1次,并且即使在多线程的环境下,dispatch_once也可以保证线程安全。
/** * 一次性代码(只执行一次)dispatch_once */ - (void)once { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 只执行1次的代码(这里面默认是线程安全的) }); }

    推荐阅读