iOS的三种多线程技术:
2013-10-07 21:54
302 查看
1. NSThread
1> 类方法 detachNewThreadSelector
直接启动线程,调用选择器方法
2> 成员方法 initWithTarget
需要使用start方法,才能启动实例化出来的线程
优点:简单
缺点:
1 控制线程的生命周期比较困难
2 控制并发线程数
3 先后顺序困难
例如:下载图片(后台线程) -> 滤镜美化(后台线程) -> 更新UI(主线程)
2. NSOperation
1> NSInvocationOperation
2> NSBlockOperation
定义完Operation之后,将操作添加到NSOperationQueue即可启动线程,执行任务
使用:
1> setMaxConcurrentOperationCount 可以控制同时并发的线程数量
2> addDependency 可以指定线程之间的依赖关系,从而达到控制线程执行顺序的目的
提示:
要更新UI,需要使用[NSOperationQueue mainQueue]addOperationWithBlock:
在主操作队列中更新界面
3. GCD
1) 全局global队列
方法:dispatch_get_global_queue(获取全局队列)
优先级:DISPATCH_QUEUE_PRIORITY_DEFAULT
所有添加到主队列中的任务都是并发执行的
2) 串行队列
方法:dispatch_queue_create(创建串行队列,串行队列不能够获取)
所有添加到串行队列中的任务都是顺序执行的
3) 主队列
主线程队列
方法:dispatch_get_main_queue(获取主队列)
所有添加到主队列中的任务都是在主线程中执行的
在gcd中,同步还是异步取决于任务执行所在的队列,更方法名没有关系
获取队列的方法:
全局队列(可能会开启多条线程)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
串行队列(只可能会开启一条线程)
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
主队列
dispatch_get_main_queue();
多线程使用注意事项:
线程使用不是无节制的
iOS中的主线程的堆栈大小是1M
从第二个线程开始都是512KB
这些数值不能通过编译器开关或线程API函数更改
只有主线程有直接修改UI的能力
1> 类方法 detachNewThreadSelector
直接启动线程,调用选择器方法
2> 成员方法 initWithTarget
需要使用start方法,才能启动实例化出来的线程
优点:简单
缺点:
1 控制线程的生命周期比较困难
2 控制并发线程数
3 先后顺序困难
例如:下载图片(后台线程) -> 滤镜美化(后台线程) -> 更新UI(主线程)
2. NSOperation
1> NSInvocationOperation
2> NSBlockOperation
定义完Operation之后,将操作添加到NSOperationQueue即可启动线程,执行任务
使用:
1> setMaxConcurrentOperationCount 可以控制同时并发的线程数量
2> addDependency 可以指定线程之间的依赖关系,从而达到控制线程执行顺序的目的
// Dependency依赖 // 提示:依赖关系可以多重依赖 // 注意:不要建立循环依赖 [op2 addDependency:op1]; [op3 addDependency:op2];
提示:
要更新UI,需要使用[NSOperationQueue mainQueue]addOperationWithBlock:
在主操作队列中更新界面
3. GCD
1) 全局global队列
方法:dispatch_get_global_queue(获取全局队列)
优先级:DISPATCH_QUEUE_PRIORITY_DEFAULT
所有添加到主队列中的任务都是并发执行的
2) 串行队列
方法:dispatch_queue_create(创建串行队列,串行队列不能够获取)
所有添加到串行队列中的任务都是顺序执行的
3) 主队列
主线程队列
方法:dispatch_get_main_queue(获取主队列)
所有添加到主队列中的任务都是在主线程中执行的
在gcd中,同步还是异步取决于任务执行所在的队列,更方法名没有关系
获取队列的方法:
全局队列(可能会开启多条线程)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
串行队列(只可能会开启一条线程)
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
主队列
dispatch_get_main_queue();
多线程使用注意事项:
线程使用不是无节制的
iOS中的主线程的堆栈大小是1M
从第二个线程开始都是512KB
这些数值不能通过编译器开关或线程API函数更改
只有主线程有直接修改UI的能力
static Ticket *SharedInstance; @implementation Ticket /** 实现单例模型需要做三件事情 1. 使用全局静态变量记录住第一个被实例化的对象 static Ticket *SharedInstance 2. 重写allocWithZone方法,并使用dispatch_once_t,从而保证在多线程情况下, 同样只能实例化一个对象副本 3. 建立一个以shared开头的类方法实例化单例对象,便于其他类调用,同时不容易引起歧义 同样用dispatch_once_t确保只有一个副本被建立 关于被抢夺资源使用的注意事项 在多线程应用中,所有被抢夺资源的属性需要设置为原子属性 系统会在多线程抢夺时,保证该属性有且仅有一个线程能够访问 注意:使用atomic属性,会降低系统性能,在开发多线程应用时,尽量不要资源 另外,atomic属性,必须与@synchronized(同步锁)一起使用 */ // 使用内存地址实例化对象,所有实例化方法,最终都会调用此方法 // 要实例化出来唯一的对象,需要一个变量记录住第一个实例化出来的对象 + (id)allocWithZone:(NSZone *)zone { // 解决多线程中,同样只能实例化出一个对象副本 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ SharedInstance = [super allocWithZone:zone]; }); return SharedInstance; } // 建立一个单例对象,便于其他类调用 + (Ticket *)sharedTicket { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ SharedInstance = [[Ticket alloc]init]; }); return SharedInstance; } @end
@property (weak, nonatomic) UITextView *textView; @property (strong, nonatomic) NSOperationQueue *queue; @end @implementation MainViewController #pragma mark 追加多行文本框内容 - (void)appendContent:(NSString *)text { // 1. 取出textView中的当前文本内容 NSMutableString *str = [NSMutableString stringWithString:self.textView.text]; // 2. 将text追加至textView内容的末尾 [str appendFormat:@"%@\n", text]; // 3. 使用追加后的文本,替换textView中的内容 [self.textView setText:str]; // 4. 将textView滚动至视图底部,保证能够及时看到新追加的内容 NSRange range = NSMakeRange(str.length - 1, 1); [self.textView scrollRangeToVisible:range]; } - (void)viewDidLoad { [super viewDidLoad]; // 建立多行文本框 UITextView *textView = [[UITextView alloc]initWithFrame:self.view.bounds]; // 不能编辑 [textView setEditable:NO]; [self.view addSubview:textView]; self.textView = textView; // 预设可以卖30张票 [Ticket sharedTicket].tickets = 30; // 实例化操作队列 self.queue = [[NSOperationQueue alloc]init]; // 开始卖票 [self operationSales]; } #pragma mark - NSThread卖票 - (void)threadSaleTicketWithName:(NSString *)name { // 使用NSThread时,线程调用的方法千万要使用@autoreleasepool @autoreleasepool { while (YES) { @synchronized(self) { if ([Ticket sharedTicket].tickets > 0) { [Ticket sharedTicket].tickets--; NSString *str = [NSString stringWithFormat:@"剩余票数 %d 线程名称 %@", [Ticket sharedTicket].tickets, name]; // 更新UI [self performSelectorOnMainThread:@selector(appendContent:) withObject:str waitUntilDone:YES]; } else { break; } } // 模拟休息 if ([name isEqualToString:@"thread-1"]) { [NSThread sleepForTimeInterval:1.0f]; } else { [NSThread sleepForTimeInterval:0.1f]; } } } } - (void)threadSales { [NSThread detachNewThreadSelector:@selector(threadSaleTicketWithName:) toTarget:self withObject:@"thread-1"]; [NSThread detachNewThreadSelector:@selector(threadSaleTicketWithName:) toTarget:self withObject:@"thread-2"]; } #pragma mark - NSOperation卖票 - (void)operationSaleTicketWithName:(NSString *)name { while (YES) { // 同步锁synchronized要锁的范围,对被抢夺资源修改/读取的代码部分 @synchronized(self) { // 判断是否还有票 if ([Ticket sharedTicket].tickets > 0) { [Ticket sharedTicket].tickets--; // 提示,涉及到被抢夺资源的内容定义方面的操作,千万不要跨线程去处理 NSString *str = [NSString stringWithFormat:@"剩余票数 %d 线程名称 %@", [Ticket sharedTicket].tickets, name]; // 更新UI [[NSOperationQueue mainQueue]addOperationWithBlock:^{ [self appendContent:str]; }]; } else { NSLog(@"卖票完成 %@ %@", name, [NSThread currentThread]); break; } } // 模拟卖票休息 if ([name isEqualToString:@"op-1"]) { [NSThread sleepForTimeInterval:0.6f]; } else { [NSThread sleepForTimeInterval:0.4f]; } } } - (void)operationSales { // 提示,operation中没有群组任务完成通知功能 // 两个线程卖票 [self.queue setMaxConcurrentOperationCount:2]; [self.queue addOperationWithBlock:^{ [self operationSaleTicketWithName:@"op-1"]; }]; [self.queue addOperationWithBlock:^{ [self operationSaleTicketWithName:@"op-2"]; }]; [self.queue addOperationWithBlock:^{ [self operationSaleTicketWithName:@"op-3"]; }]; } #pragma mark - GCD卖票 - (void)gcdSaleTicketWithName:(NSString *)name { while (YES) { // 同步锁synchronized要锁的范围,对被抢夺资源修改/读取的代码部分 @synchronized(self) { if ([Ticket sharedTicket].tickets > 0) { [Ticket sharedTicket].tickets--; // 提示内容 NSString *str = [NSString stringWithFormat:@"剩余票数 %d, 线程名称 %@", [Ticket sharedTicket].tickets, name]; // 更新界面 dispatch_sync(dispatch_get_main_queue(), ^{ [self appendContent:str]; }); } else { break; } } // 模拟线程休眠 if ([name isEqualToString:@"gcd-1"]) { [NSThread sleepForTimeInterval:1.0f]; } else { [NSThread sleepForTimeInterval:0.2f]; } } } - (void)gcdSales { // 1) 创建全局队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 2) 创建三个个异步任务分别卖票 // dispatch_async(queue, ^{ // [self gcdSaleTicketWithName:@"gcd-1"]; // }); // dispatch_async(queue, ^{ // [self gcdSaleTicketWithName:@"gcd-2"]; // }); // dispatch_async(queue, ^{ // [self gcdSaleTicketWithName:@"gcd-3"]; // }); // 3. GCD中可以将一组相关联的操作,定义到一个群组中 // 定义到群组中之后,当所有线程完成时,可以获得通知 // 1) 定义群组 dispatch_group_t group = dispatch_group_create(); // 2) 定义群组的异步任务 dispatch_group_async(group, queue, ^{ [self gcdSaleTicketWithName:@"gcd-1"]; }); dispatch_group_async(group, queue, ^{ [self gcdSaleTicketWithName:@"gcd-2"]; }); // 3) 群组任务完成通知 dispatch_group_notify(group, queue, ^{ NSLog(@"卖完了"); }); } @end
相关文章推荐
- ios-->iTunes同步应用失败
- freetype2.4.6在iOS上各版本的编译脚本
- iOS设置系统语言
- iOS 单例用法
- iOS中的下拉刷新SVPullToRefresh
- Ngios plugin for cacti(NPC)
- rhel6-nagios的安装与配置
- iOS 7: 如何为iPhone 5S编译64位应用
- [转]关于ios 推送功能的终极解决
- DSP/BIOS中的线程
- iOS 带文字和图片的button
- iOS CoreData
- iOS多线程编程指南(四)线程同步
- iOS多线程编程指南(三)Run Loop
- iOS多线程编程指南(二)线程管理
- ios7 留白
- iOS多线程编程指南(一)关于多线程编程
- IOS 多线程编程 指南 及 【中文版完整翻译】
- iOS中堆和栈的区别
- 苹果IPhone手机由于更新了IOS7 Beta测试版导致“激活出错”后,如何还原电话本和照片方法