IOS开发之多线程队列
2015-08-01 18:49
309 查看
串行队列
特点
以先进先出的方式,
顺序调度队列中的任务执行
无论队列中所指定的执行任务函数是同步还是异步,都会等待前一个任务执行完成后,再调度后面的任务
队列创建
dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_SERIAL);dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL);
串行队列演练
串行队列 同步执行/** 提问:是否开线程?是否顺序执行?come here 的位置? */- (void)gcdDemo1 { // 1. 队列 dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_SERIAL); // 2. 执行任务 for (int i = 0; i < 10; ++i) { NSLog(@"--- %d", i); dispatch_sync(q, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); } NSLog(@"come here");}
串行队列 异步执行
/** 提问:是否开线程?是否顺序执行?come here 的位置? */- (void)gcdDemo2 { // 1. 队列 dispatch_queue_t q = dispatch_queue_create("itheima", NULL); // 2. 执行任务 for (int i = 0; i < 10; ++i) { NSLog(@"--- %@ %d", [NSThread currentThread], i); dispatch_async(q, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); } NSLog(@"come here");}
并发队列
特点
以先进先出的方式,
并发调度队列中的任务执行
如果当前调度的任务是
同步执行的,会等待任务执行完成后,再调度后续的任务
如果当前调度的任务是
异步执行的,同时底层线程池有可用的线程资源,会再新的线程调度后续任务的执行
队列创建
dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_CONCURRENT);
并发队列演练
并发队列 异步执行/** 提问:是否开线程?是否顺序执行?come here 的位置? */- (void)gcdDemo3 { // 1. 队列 dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_CONCURRENT); // 2. 执行任务 for (int i = 0; i < 10; ++i) { dispatch_async(q, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); } NSLog(@"come here");}
并发队列 同步执行
/** 提问:是否开线程?是否顺序执行?come here 的位置? */- (void)gcdDemo4 { // 1. 队列 dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_CONCURRENT); // 2. 执行任务 for (int i = 0; i < 10; ++i) { dispatch_sync(q, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); NSLog(@"---> %i", i); } NSLog(@"come here");}
主队列
特点
专门用来在主线程上调度任务的队列不会开启线程
以
先进先出的方式,在
主线程空闲时才会调度队列中的任务在主线程执行
如果当前主线程正在有任务执行,那么无论主队列中当前被添加了什么任务,都不会被调度
队列获取
主队列是负责在主线程调度任务的会随着程序启动一起创建
主队列只需要获取不用创建
dispatch_queue_t queue = dispatch_get_main_queue();
主队列演练
主队列,异步执行- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self gcdDemo1]; [NSThread sleepForTimeInterval:1]; NSLog(@"over");}- (void)gcdDemo1 { dispatch_queue_t queue = dispatch_get_main_queue(); for (int i = 0; i < 10; ++i) { dispatch_async(queue, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); NSLog(@"---> %d", i); } NSLog(@"come here");}2015-07-13 00:44:57.241 testGCD线程[37988:581895] ---> 02015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 12015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 22015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 32015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 42015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 52015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 62015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 72015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 82015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 92015-07-13 00:44:57.242 testGCD线程[37988:581895] come here2015-07-13 00:44:58.243 testGCD线程[37988:581895] over2015-07-13 00:44:58.244 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 02015-07-13 00:44:58.244 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 12015-07-13 00:44:58.244 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 22015-07-13 00:44:58.244 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 32015-07-13 00:44:58.245 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 42015-07-13 00:44:58.245 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 52015-07-13 00:44:58.245 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 62015-07-13 00:44:58.245 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 72015-07-13 00:44:58.245 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 82015-07-13 00:44:58.245 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 9
在
主线程空闲时才会调度队列中的任务在主线程执行
主队列,同步执行
// MARK: 主队列,同步任务- (void)gcdDemo6 { // 1. 队列 dispatch_queue_t q = dispatch_get_main_queue(); NSLog(@"!!!"); // 2. 同步 dispatch_sync(q, ^{ NSLog(@"%@", [NSThread currentThread]); }); NSLog(@"come here");}
主队列和
主线程相互等待会造成死锁
同步任务的作用
同步任务,可以让其他异步执行的任务,
依赖某一个同步任务
例如:在用户登录之后,再异步下载文件!
- (void)gcdDemo1 { dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^{ NSLog(@"登录 %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"下载 A %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"下载 B %@", [NSThread currentThread]); });}
代码改造,让登录也在异步执行
- (void)gcdDemo2 { dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_CONCURRENT); void (^task)() = ^{ dispatch_sync(queue, ^{ NSLog(@"登录 %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"下载 A %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"下载 B %@", [NSThread currentThread]); }); }; dispatch_async(queue, task);}
主队列调度同步队列不死锁
- (void)gcdDemo3 { dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_CONCURRENT); void (^task)() = ^ { dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"死?"); }); }; dispatch_async(queue, task);}
主队列在
主线程空闲时才会调度队列中的任务在主线程执行
Barrier 异步
主要用于在多个异步操作完成之后,统一对
非线程安全的对象进行更新
适合于
大规模的 I/O操作
代码演练
准备工作@interface ViewController () { // 加载照片队列 dispatch_queue_t _photoQueue;}@property (nonatomic, strong) NSMutableArray *photoList;@end- (NSMutableArray *)photoList { if (_photoList == nil) { _photoList = [[NSMutableArray alloc] init]; } return _photoList;}
NSMutableArray是非线程安全的
viewDidLoad
- (void)viewDidLoad { [super viewDidLoad]; _photoQueue = dispatch_queue_create("com.itheima.com", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 20; ++i) { [self loadPhotos:i]; }}
模拟下载照片并在完成后添加到数组
- (void)loadPhotos:(int)index { dispatch_async(_photoQueue, ^{ [NSThread sleepForTimeInterval:1.0]; NSString *fileName = [NSString stringWithFormat:@"%02d.jpg", index % 10 + 1]; NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil]; UIImage *image = [UIImage imageWithContentsOfFile:path]; [self.photoList addObject:image]; NSLog(@"添加照片 %@", fileName); });}
运行测试
由于
NSMutableArray是非线程安全的,如果出现两个线程在同一时间向数组中添加对象,会出现程序崩溃的情况
解决办法
NSLog(@"添加照片 %@", fileName);dispatch_barrier_async(_photoQueue, ^{ [self.photoList addObject:image]; NSLog(@"OK %@", [NSThread currentThread]);});
使用
dispatch_barrier_async添加的 block 会在之前添加的 block 全部运行结束之后,才在同一个线程顺序执行,从而保证对非线程安全的对象进行正确的操作!
Barrier 工作示意图
注意:
dispatch_barrier_async必须使用自定义队列,否则执行效果和全局队列一致
全局队列
是系统为了方便程序员开发提供的,其工作表现与并发队列一致
全局队列 & 并发队列的区别
全局队列没有名称无论 MRC & ARC 都不需要考虑释放
日常开发中,建议使用全局队列
并发队列有名字,和
NSThread的
name属性作用类似
如果在 MRC 开发时,需要使用
dispatch_release(q);释放相应的对象
dispatch_barrier必须使用自定义的并发队列
开发第三方框架时,建议使用并发队列
全局队列 异步任务
/** 提问:是否开线程?是否顺序执行?come here 的位置? */- (void)gcdDemo8 { // 1. 队列 dispatch_queue_t q = dispatch_get_global_queue(0, 0); // 2. 执行任务 for (int i = 0; i < 10; ++i) { dispatch_async(q, ^{ NSLog(@"%@ - %d", [NSThread currentThread], i); }); } NSLog(@"come here");}
运行效果与并发队列相同
参数
服务质量(队列对任务调度的优先级)/iOS 7.0 之前,是优先级iOS 8.0(新增,暂时不能用,今年年底)
QOS_CLASS_USER_INTERACTIVE0x21, 用户交互(希望最快完成-不能用太耗时的操作)
QOS_CLASS_USER_INITIATED0x19, 用户期望(希望快,也不能太耗时)
QOS_CLASS_DEFAULT0x15, 默认(用来底层重置队列使用的,不是给程序员用的)
QOS_CLASS_UTILITY0x11, 实用工具(专门用来处理耗时操作!)
QOS_CLASS_BACKGROUND0x09, 后台
QOS_CLASS_UNSPECIFIED0x00, 未指定,可以和iOS 7.0 适配
iOS 7.0
DISPATCH_QUEUE_PRIORITY_HIGH2 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT0 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW(-2) 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUNDINT16_MIN 后台优先级
为未来保留使用的,应该永远传入0
结论:如果要适配 iOS 7.0 & 8.0,使用以下代码:
dispatch_get_global_queue(0, 0);
延迟操作
// MARK: - 延迟执行- (void)delay { /** 从现在开始,经过多少纳秒,由"队列"调度异步执行 block 中的代码 参数 1. when 从现在开始,经过多少纳秒 2. queue 队列 3. block 异步执行的任务 */ dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)); void (^task)() = ^ { NSLog(@"%@", [NSThread currentThread]); }; // 主队列// dispatch_after(when, dispatch_get_main_queue(), task); // 全局队列// dispatch_after(when, dispatch_get_global_queue(0, 0), task); // 串行队列 dispatch_after(when, dispatch_queue_create("itheima", NULL), task); NSLog(@"come here");}- (void)after { [self.view performSelector:@selector(setBackgroundColor:) withObject:[UIColor orangeColor] afterDelay:1.0]; NSLog(@"come here");}
一次性执行
有的时候,在程序开发中,有些代码只想从程序启动就只执行一次,典型的应用场景就是“单例”
// MARK: 一次性执行- (void)once { static dispatch_once_t onceToken; NSLog(@"%ld", onceToken); dispatch_once(&onceToken, ^{ [NSThread sleepForTimeInterval:1.0]; NSLog(@"一次性吗?"); }); NSLog(@"come here");}
dispatch 内部也有一把锁,是能够保证线程安全的!而且是苹果公司推荐使用的
以下代码用于测试多线程的一次性执行
- (void)demoOnce { for (int i = 0; i < 10; ++i) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self once]; }); }}
单例测试
单例的特点
在内存中只有一个实例提供一个全局的访问点
单例实现
// 使用 dispatch_once 实现单例+ (instancetype)sharedSingleton { static id instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; }); return instance;}// 使用互斥锁实现单例+ (instancetype)sharedSync { static id syncInstance; @synchronized(self) { if (syncInstance == nil) { syncInstance = [[self alloc] init]; } } return syncInstance;}
面试时只要实现上面
sharedSingleton方法即可
单例测试
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { long largeNumber = 1000 * 1000; // 测试互斥锁 CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); for (long i = 0; i < largeNumber; ++i) { [Singleton sharedSync]; } NSLog(@"互斥锁: %f", CFAbsoluteTimeGetCurrent() - start); // 测试 dispatch_once start = CFAbsoluteTimeGetCurrent(); for (long i = 0; i < largeNumber; ++i) { [Singleton sharedSingleton]; } NSLog(@"dispatch_once: %f", CFAbsoluteTimeGetCurrent() - start);}
调度组
常规用法
- (void)group1 { // 1. 调度组 dispatch_group_t group = dispatch_group_create(); // 2. 队列 dispatch_queue_t q = dispatch_get_global_queue(0, 0); // 3. 将任务添加到队列和调度组 dispatch_group_async(group, q, ^{ [NSThread sleepForTimeInterval:1.0]; NSLog(@"任务 1 %@", [NSThread currentThread]); }); dispatch_group_async(group, q, ^{ NSLog(@"任务 2 %@", [NSThread currentThread]); }); dispatch_group_async(group, q, ^{ NSLog(@"任务 3 %@", [NSThread currentThread]); }); // 4. 监听所有任务完成 dispatch_group_notify(group, q, ^{ NSLog(@"OVER %@", [NSThread currentThread]); }); // 5. 判断异步 NSLog(@"come here");}
enter & leavel
// MARK: - 调度组 2- (void)group2 { // 1. 调度组 dispatch_group_t group = dispatch_group_create(); // 2. 队列 dispatch_queue_t q = dispatch_get_global_queue(0, 0); // dispatch_group_enter & dispatch_group_leave 必须成对出现 dispatch_group_enter(group); dispatch_group_async(group, q, ^{ NSLog(@"任务 1 %@", [NSThread currentThread]); // dispatch_group_leave 必须是 block 的最后一句 dispatch_group_leave(group); }); dispatch_group_enter(group); dispatch_group_async(group, q, ^{ NSLog(@"任务 2 %@", [NSThread currentThread]); // dispatch_group_leave 必须是 block 的最后一句 dispatch_group_leave(group); }); // 4. 阻塞式等待调度组中所有任务执行完毕 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // 5. 判断异步 NSLog(@"OVER %@", [NSThread currentThread]);}
相关文章推荐
- IOS autoLayout之使用VFL语言进行代码自动布局
- hdu 1017 A Mathematical Curiosity
- ios 为图片加上水印详解
- iOS 扩展机制category与associative
- iOS内存管理机制
- iOS7.0及以上图片转换成base64编码的方法(oc)
- iOS-应用管理 点击按钮下载动画
- IOS 7 自动布局详解(一)
- iOS-mvc分文件夹
- iOS MKMapView 以某一坐标为中心进行缩放
- ios7 二维码图片生成
- iOS中判断两个圆是否重叠
- iOS开发之禁用ios7 手势滑动返回功能
- ios-利用xib重新写 应用管理
- KVC 与 KVO
- 提高iOS开发效率的方法和工具
- iOS alloc和new的区别与联系
- 只会左键断点?是时候试试这样那样断点了
- OC_NSString和NSMutableString
- iOS开发文档