1.基本概念
同步任务:在当前线程按顺序执行,不开启新的线程
异步任务:有开新线程的欲望
串行队列:一个一个执行
并行队列:多个任务同时执行
---------------------------------------------------------
串行队列-同步任务 在当前线程执行
串行队列-异步任务 开一个子线程执行
/** 串行队列 */ - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ //串行队列,同步方法 [self serailSync]; //串行队列,异步方法 // [self serailAsync]; } /** 串行队列,异步方法 1.打印顺序 : 从上到下依次执行,它是串行队列 2.在哪条线程上执行:在子线程,因为它是异步执行,异步,就是不在当前线程里面执行 应用场景:耗时间,有顺序的任务 1.登录--->2.付费--->3.才能看 */ - (void)serailAsync{ //1.创建一个串行队列 /** 参数1:队列的标识fuhao,一般是公司的域名倒写 www.itheima.com 参数2:队列的类型 DISPATCH_QUEUE_SERIAL 串行队列 DISPATCH_QUEUE_CONCURRENT 并发队列 */ dispatch_queue_t serialQueue = dispatch_queue_create("com.itheima.queue1", DISPATCH_QUEUE_SERIAL); //2.创建三个任务 void (^task1) () = ^(){ NSLog(@"task1---%@",[NSThread currentThread]); }; void (^task2) () = ^(){ NSLog(@"task2---%@",[NSThread currentThread]); }; void (^task3) () = ^(){ NSLog(@"task3---%@",[NSThread currentThread]); }; //3.把我们上面创建好的三个任务,添加到队列中去,这个队列就会自己开始调用我们的任务 //异步方法执行 dispatch_async(serialQueue, task1); dispatch_async(serialQueue, task2); dispatch_async(serialQueue, task3); } /** 串行队列,同步方法 1.打印顺序 : 从上到下,依次打印,因为串行的 2.在哪条线程上执行:主线程,因为是同步方法,所以在当前线程里面执行,恰好当前线程是主线程,所以它就在主线程上面执行 应用场景:开发中很少用 */ - (void)serailSync{ //1.创建一个串行队列 /** 参数1:队列的标识fuhao,一般是公司的域名倒写 www.itheima.com 参数2:队列的类型 DISPATCH_QUEUE_SERIAL 串行队列 DISPATCH_QUEUE_CONCURRENT 并发队列 */ dispatch_queue_t serialQueue = dispatch_queue_create("com.itheima.queue1", DISPATCH_QUEUE_SERIAL); //2.创建三个任务 void (^task1) () = ^(){ NSLog(@"task1---%@",[NSThread currentThread]); }; void (^task2) () = ^(){ NSLog(@"task2---%@",[NSThread currentThread]); }; void (^task3) () = ^(){ NSLog(@"task3---%@",[NSThread currentThread]); }; //3.把我们上面创建好的三个任务,添加到队列中去,这个队列就会自己开始调用我们的任务 //同步方法执行 dispatch_sync(serialQueue, task1); dispatch_sync(serialQueue, task2); dispatch_sync(serialQueue, task3); }
并行队列-同步任务 在当前线程执行
并行队列-异步任务 开多个子线程执行
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ //1.并发队列,同步任务 //[self concurrentSync]; //2.并发队列,异步任务 [self concurrentAsync]; } - (void)concurrentAsync{ /** 并发队列,异步方法 1.打印顺序 :无序的 2.在哪条线程上执行:在子线程上执行,第一个任务,都在它自己的线程上执行 开N条,它是由底层可调度线程池来决定的,可调度线程池它是有一个重用机制 应用场景: 半月转 开头 中间 结尾 下载电视剧某一集的时候,可以把我们的片头,片尾,中间内容 一起下 最后,拼接组合一下,就可以播放了 片头,中间内容,片尾 */ //1.创建一个并发的队列 dispatch_queue_t concurrentQueue = dispatch_queue_create("com.itheima.queue2", DISPATCH_QUEUE_CONCURRENT); //2.创建三个任务 void (^task1) () = ^(){ NSLog(@"task1---%@",[NSThread currentThread]); }; void (^task2) () = ^(){ NSLog(@"task2---%@",[NSThread currentThread]); }; void (^task3) () = ^(){ NSLog(@"task3---%@",[NSThread currentThread]); }; //3.将我们的三个任务,添加到队列中 //异步方法去执行 dispatch_async(concurrentQueue, task1); dispatch_async(concurrentQueue, task2); dispatch_async(concurrentQueue, task3); } /** 并发队列,同步方法 1.打印顺序 : 依次执行,因为它是同步的 2.在哪条线程上执行:主线程,因为它是同步方法,它就在当前线程里面执行,主线程,依次执行 当它遇到同步的时候,并发队列,还是依次执行 所以说,方法的优先级会比队列的优先级高 * 只要是同步方法,都只会在当前线程里面执行,不会开子线程 应用场景: 开发中几乎不用 */ - (void)concurrentSync{ //1.创建一个并发的队列 dispatch_queue_t concurrentQueue = dispatch_queue_create("com.itheima.queue2", DISPATCH_QUEUE_CONCURRENT); //2.创建三个任务 void (^task1) () = ^(){ NSLog(@"task1---%@",[NSThread currentThread]); }; void (^task2) () = ^(){ NSLog(@"task2---%@",[NSThread currentThread]); }; void (^task3) () = ^(){ NSLog(@"task3---%@",[NSThread currentThread]); }; //3.将我们的三个任务,添加到队列中 //同步方法去执行 dispatch_sync(concurrentQueue, task1); dispatch_sync(concurrentQueue, task2); dispatch_sync(concurrentQueue, task3); }
全局队列不需要创建,直接get就能用,执行效果与并行队列相同
主线程队列,保证操作在主线程上执行
异步任务,顺序执行,会先执行后面的代码
主线程队列中存在同步任务,永远不会被执行,被阻塞了
主队列只有在主线程空闲的时候,才会去调度它里面的任务去执行,死等
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ //1.主队列,异步 [self mainAsync]; //2.主队列,同步 //[self mainSync]; } /** 主队列,同步任务有问题,不能用 `主队列`只有在`主线程空闲`的时候,才会去调度它里面的任务去执行 */ - (void)mainSync{ //只是用来调试,说明我们来到了这个方法 NSLog(@"%s",__func__); //1.获取主队列 dispatch_queue_t mainQueue = dispatch_get_main_queue(); //2.搞三个任务 void (^task1) () = ^(){ NSLog(@"task1===%@",[NSThread currentThread]); }; void (^task2) () = ^(){ NSLog(@"task2===%@",[NSThread currentThread]); }; void (^task3) () = ^(){ NSLog(@"task3===%@",[NSThread currentThread]); }; //3.将任务添加到主队列中 dispatch_sync(mainQueue, task1); dispatch_sync(mainQueue, task2); dispatch_sync(mainQueue, task3); NSLog(@"mainSync----end"); } /** 主队列,异步任务 1.执行顺序:依次执行,因为它在主线程里面执行 * 似乎与我们的异步任务有所冲突,但是因为它是主队列,所以,只在主线程里面执行 2.是否会开线程:不会开,因为它在主线程里面执行 应用场景: 当做了耗时间操作之后,当我们需要回到主线程更新UI的时候,就非它不可 */ - (void)mainAsync{ //1.获取主队列 dispatch_queue_t mainQueue = dispatch_get_main_queue(); //2.搞三个任务 void (^task1) () = ^(){ NSLog(@"task1===%@",[NSThread currentThread]); }; void (^task2) () = ^(){ NSLog(@"task2===%@",[NSThread currentThread]); }; void (^task3) () = ^(){ NSLog(@"task3===%@",[NSThread currentThread]); }; //3.将任务添加到主队列中 dispatch_async(mainQueue, task1); dispatch_async(mainQueue, task2); dispatch_async(mainQueue, task3); NSLog(@"---mainAsync---end"); }
2.常用代码
/** 如果要在子线程中执行 除了`主队列`都可以,串行,并发,全局都可以 去主线程只有一个方法,那就是主队列,异步 */ dispatch_async(dispatch_get_global_queue(0, 0), ^{ //1.去子线程里面加载图片 NSLog(@"假装在下载...%@",[NSThread currentThread]); [NSThread sleepForTimeInterval:3.0]; //2.去主线程更新UI dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"%@----更新UI",[NSThread currentThread]); }); });
3.同步的作用
/** 同步的作用:保证我们任务执行的先后顺序 1.登录 2.同时下载三部小电影 */ - (void)execLongTimeOperation{ dispatch_async(dispatch_get_global_queue(0, 0), ^{ //1.登录,同步在当前线程里面工作 dispatch_sync(dispatch_get_global_queue(0, 0), ^{ NSLog(@"%@----登录...",[NSThread currentThread]); [NSThread sleepForTimeInterval:3.0]; }); //2.同时下载三部小电影(不需要先后顺序) dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"downLoadA----%@",[NSThread currentThread]); }); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"downLoadV----%@",[NSThread currentThread]); }); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"downLoadI----%@",[NSThread currentThread]); }); }); }
4.延迟操作
/** 参数1.延时多少纳秒,整个延迟3秒 参数2:是决定,参加在哪个线程里面调用 参数3:任务执行的代码块 dispatch_after 异步的 应用场景: 动画 */ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"---下载完毕---%@",[NSThread currentThread]); }); NSLog(@"---end---");
5.单例
+ (instancetype)sharedNetWorkToos;
static id _instance; + (instancetype)sharedNetWorkToos{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [[self alloc] init]; }); return _instance; } + (instancetype)allocWithZone:(struct _NSZone *)zone{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [super allocWithZone:zone]; }); return _instance; } - (id)copyWithZone:(NSZone *)zone{ return _instance; }
6.调度组,组内的线程全部执行完,会到dispatch_group_notify定义的方法中
//1.创建一个调度组 dispatch_group_t group = dispatch_group_create(); //2.获取全局队列 dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); //3.三个下载任务 void (^task1) () = ^(){ NSLog(@"%@----下载片头",[NSThread currentThread]); }; void (^task2) () = ^(){ NSLog(@"%@----下载中间的内容",[NSThread currentThread]); [NSThread sleepForTimeInterval:5.0]; NSLog(@"--下载中间内容完毕---"); }; void (^task3) () = ^(){ NSLog(@"%@----下载片尾",[NSThread currentThread]); }; //3.需要将我们的队列和任务,加入组内去监控 dispatch_group_async(group, globalQueue, task1); dispatch_group_async(group, globalQueue, task2); dispatch_group_async(group, globalQueue, task3); //4.监听的函数 /** 参数1:组 参数2:参数3在哪个线程里面执行 参数3:组内完全下载完毕之后,需要执行的代码 */ dispatch_group_notify(group, dispatch_get_main_queue(), ^{ //表示组内的所有任务都完成之后,会来到这里 NSLog(@"把下好的视频按照顺序拼接好,然后显示在UI去播放"); });
调度组的实现原理
/** 应用场景 比如我去同时开三个线程下载视频,只有当三个视频完全下载完毕之后,我才能做后续的事, 这个就需要用到调度组,这个调度组,就能监听它里面的任务是否都执行完毕 */ - (void)execGroupDispatch{ //1.创建一个调度组 dispatch_group_t group = dispatch_group_create(); //2.获取全局队列 dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); //3.三个下载任务 dispatch_group_enter(group); //引用计数+1 void (^task1) () = ^(){ NSLog(@"%@----下载片头",[NSThread currentThread]); dispatch_group_leave(group); //引用计数-1 }; dispatch_group_enter(group); //引用计数+1 void (^task2) () = ^(){ NSLog(@"%@----下载中间的内容",[NSThread currentThread]); [NSThread sleepForTimeInterval:5.0]; NSLog(@"--下载中间内容完毕---"); dispatch_group_leave(group);//引用计数-1 }; void (^task3) () = ^(){ NSLog(@"%@----下载片尾",[NSThread currentThread]); }; //3.需要将我们的队列和任务,加入组内去监控 dispatch_group_async(group, globalQueue, task1); dispatch_group_async(group, globalQueue, task2); dispatch_group_async(group, globalQueue, task3); //4.监听的函数 /** 参数1:组 参数2:参数3在哪个线程里面执行 参数3:组内完全下载完毕之后,需要执行的代码 */ dispatch_group_notify(group, dispatch_get_main_queue(), ^{ //表示组内的所有任务都完成之后,会来到这里 NSLog(@"把下好的视频按照顺序拼接好,然后显示在UI去播放"); }); }
7.定时器与运行循环
@interface ViewController () @property (nonatomic, assign) int count; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self performSelectorInBackground:@selector(subThreadRun) withObject:nil]; } - (void)subThreadRun{ NSLog(@"%s----%@",__func__,[NSThread currentThread]); //1.定义了一个定时器 NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeEvent) userInfo:nil repeats:YES]; //2.将我们的定时器加入到运行循环,只有加入到当前的运行循环里面去,他才知道你这个时候,有一个定时任务 /** NSDefaultRunLoopMode 当拖动的时候,它会停掉 因为这种模式是互斥的 forMode:UITrackingRunLoopMode 只有输入的时候,它才会去执行定时器任务 NSRunLoopCommonModes 包含了前面两种 */ //[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; //[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; //下载,定时源事件,输入源事件,如果放在子线程里面,如果想要它执行任务,就必须开启子线程的运行循环 CFRunLoopRun(); } - (void)timeEvent{ self.count ++; NSLog(@"%d",self.count); if (self.count==5) { NSLog(@"---guale---"); //停止当前的运行循环 CFRunLoopStop(CFRunLoopGetCurrent()); } }