iOS多线程编程——NSOperation的使用
2016-07-14 23:40
477 查看
NSOperation是苹果提供给我们的一套多线程解决方案。实际上NSOperation是基于GCD更高一层的封装,但是比GCD更简单易用、代码可读性也更高。NSOperation需要配合NSOperationQueue来实现多线程。因为默认情况下,NSOperation单独使用时系统同步执行操作,并没有开辟新线程的能力,只有配合NSOperationQueue才能实现异步执行。因为NSOperation是基于GCD的,那么使用起来也和GCD差不多,其中,NSOperation相当于GCD中的任务,而NSOperationQueue则相当于GCD中的队列。NSOperation实现多线程的使用步骤分为三步:(1)创建任务:先将需要执行的操作封装到一个NSOperation对象中。(2)创建队列:创建NSOperationQueue对象。(3)将任务加入到队列中:然后将NSOperation对象添加到NSOperationQueue中。之后系统就会自动将NSOperationQueue中的NSOperation取出来,在新线程中执行操作。
1、NSOperationQueue一共有两种队列:主队列、其他队列。其中其他队列同时包含了串行、并发功能。(1)凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行。(2)添加到其他队列中的任务(NSOperation),就会自动放到子线程中执行,且 同时包含了:串行、并发功能。2、队列中的最大并发数maxConcurrentOperationCount(默认为-1)(1)当maxConcurrentOperationCount为-1,表示不进行限制,默认为并发执行。(2)当maxConcurrentOperationCount为1时,进行串行执行。(3)当maxConcurrentOperationCount大于1时,进行并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会自动调整。3、队列中的任务间的依赖比如说有A、B两个操作,其中A执行完操作,B才能执行操作,那么就需要让B依赖于A。(1)不要建立循环依赖,会造成死锁,原因同循环引用(2)使用依赖建议只使用NSInvocationOperation,NSInvocationOperation和NSBlockOperation混用会导致依赖关系无法正常实现。(3)依赖关系不光在同队列中生效,不同队列的NSOperation对象之前设置的依赖关系一样会生效4、优先级,建议使用默认级别5、取消等操作- (void)cancel; NSOperation提供的方法,可取消单个操作- (void)cancelAllOperations; NSOperationQueue提供的方法,可以取消队列的所有操作,所有任务取消,包括正在执行的,还未执行的。- (void)setSuspended:(BOOL)b; 可设置任务的暂停和恢复,YES代表暂停队列,NO代表恢复队列- (BOOL)isSuspended; 判断暂停状态,暂停是指当前队列中正在执行,或还未执行的任务;恢复是指继续执行暂停后的任务。
注意:(1)单独使用任务NSOperation时,是在主线程中同步执行的。(2)NSInvocationOperation实例必须加到 NSOperationQueue 的方法" - (void)addOperation:(NSOperation *)op; "中才能开始执行.(3)NSInvocationOperation子线程的停止,可通过 NSOperationQueue 的方法" - (void)cancelAllOperations; "停止。
代码示例1、同步任务// 同步,在主线程中执行
- (void)runSynchronization
{
// 创建任务
// 方法1
// 使用子类 NSInvocationOperation 对象,在主线程中执行
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@110];
[operation1 start];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@120];
[operation2 start];
}
- (void)showTime:(id)object
{
// 模拟操作时间耗时
double time = (arc4random() % 10 + 10 / 1000);
[NSThread sleepForTimeInterval:time];
NSLog(@"主线程:%d, 线程名称:%@, object = %@, time %f", [NSThread isMainThread], [NSThread currentThread], object, time);
}
2018-03-06 17:59:44.794 DemoThread[7885:1445277] 主线程:1, 线程名称:<NSThread: 0x6000000643c0>{number = 1, name = main}, object = 110, time 9.000000
2018-03-06 17:59:49.796 DemoThread[7885:1445277] 主线程:1, 线程名称:<NSThread: 0x6000000643c0>{number = 1, name = main}, object = 120, time 5.000000
- (void)runMainQueue
{
// 1 创建队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
[queue addOperationWithBlock:^{
[self showTime:@210];
}];
[queue addOperationWithBlock:^{
[self showTime:@220];
}];
}
- (void)showTime:(id)object
{
// 模拟操作时间耗时
double time = (arc4random() % 10 + 10 / 1000);
[NSThread sleepForTimeInterval:time];
NSLog(@"主线程:%d, 线程名称:%@, object = %@, time %f", [NSThread isMainThread], [NSThread currentThread], object, time);
}
2018-03-06 18:01:30.895 DemoThread[7918:1447374] 主线程:1, 线程名称:<NSThread: 0x608000065080>{number = 1, name = main}, object = 210, time 2.000000
2018-03-06 18:01:35.897 DemoThread[7918:1447374] 主线程:1, 线程名称:<NSThread: 0x608000065080>{number = 1, name = main}, object = 220, time 5.000000
1、NSOperationQueue一共有两种队列:主队列、其他队列。其中其他队列同时包含了串行、并发功能。(1)凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行。(2)添加到其他队列中的任务(NSOperation),就会自动放到子线程中执行,且 同时包含了:串行、并发功能。2、队列中的最大并发数maxConcurrentOperationCount(默认为-1)(1)当maxConcurrentOperationCount为-1,表示不进行限制,默认为并发执行。(2)当maxConcurrentOperationCount为1时,进行串行执行。(3)当maxConcurrentOperationCount大于1时,进行并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会自动调整。3、队列中的任务间的依赖比如说有A、B两个操作,其中A执行完操作,B才能执行操作,那么就需要让B依赖于A。(1)不要建立循环依赖,会造成死锁,原因同循环引用(2)使用依赖建议只使用NSInvocationOperation,NSInvocationOperation和NSBlockOperation混用会导致依赖关系无法正常实现。(3)依赖关系不光在同队列中生效,不同队列的NSOperation对象之前设置的依赖关系一样会生效4、优先级,建议使用默认级别5、取消等操作- (void)cancel; NSOperation提供的方法,可取消单个操作- (void)cancelAllOperations; NSOperationQueue提供的方法,可以取消队列的所有操作,所有任务取消,包括正在执行的,还未执行的。- (void)setSuspended:(BOOL)b; 可设置任务的暂停和恢复,YES代表暂停队列,NO代表恢复队列- (BOOL)isSuspended; 判断暂停状态,暂停是指当前队列中正在执行,或还未执行的任务;恢复是指继续执行暂停后的任务。
注意:(1)单独使用任务NSOperation时,是在主线程中同步执行的。(2)NSInvocationOperation实例必须加到 NSOperationQueue 的方法" - (void)addOperation:(NSOperation *)op; "中才能开始执行.(3)NSInvocationOperation子线程的停止,可通过 NSOperationQueue 的方法" - (void)cancelAllOperations; "停止。
代码示例1、同步任务// 同步,在主线程中执行
- (void)runSynchronization
{
// 创建任务
// 方法1
// 使用子类 NSInvocationOperation 对象,在主线程中执行
NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@110];
[operation1 start];
NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@120];
[operation2 start];
}
- (void)showTime:(id)object
{
// 模拟操作时间耗时
double time = (arc4random() % 10 + 10 / 1000);
[NSThread sleepForTimeInterval:time];
NSLog(@"主线程:%d, 线程名称:%@, object = %@, time %f", [NSThread isMainThread], [NSThread currentThread], object, time);
}
2018-03-06 17:59:44.794 DemoThread[7885:1445277] 主线程:1, 线程名称:<NSThread: 0x6000000643c0>{number = 1, name = main}, object = 110, time 9.000000
2018-03-06 17:59:49.796 DemoThread[7885:1445277] 主线程:1, 线程名称:<NSThread: 0x6000000643c0>{number = 1, name = main}, object = 120, time 5.000000
// 同步,在主线程中执行 - (void)runSynchronization { // 创建任务 // 使用子类 NSBlockOperation 对象,在主线程中执行 NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ [self showTime:@210]; }]; [operation1 addExecutionBlock:^{ // 并行,开辟子线程,在子线程中执行 [self showTime:@2110]; }]; [operation1 addExecutionBlock:^{ // 并行,开辟子线程,在子线程中执行 [self showTime:@2120]; }]; [operation1 start]; NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ [self showTime:@220]; }]; [operation2 addExecutionBlock:^{ // 并行,开辟子线程,在子线程中执行 [self showTime:@2210]; }]; [operation2 addExecutionBlock:^{ // 并行,开辟子线程,在子线程中执行 [self showTime:@2220]; }]; [operation2 start]; } - (void)showTime:(id)object { // 模拟操作时间耗时 double time = (arc4random() % 10 + 10 / 1000); [NSThread sleepForTimeInterval:time]; NSLog(@"主线程:%d, 线程名称:%@, object = %@, time %f", [NSThread isMainThread], [NSThread currentThread], object, time); } 2018-03-06 17:56:23.772 DemoThread[7847:1441955] 主线程:0, 线程名称:<NSThread: 0x60800007d480>{number = 3, name = (null)}, object = 2110, time 2.000000 2018-03-06 17:56:26.697 DemoThread[7847:1441684] 主线程:0, 线程名称:<NSThread: 0x600000270500>{number = 4, name = (null)}, object = 2120, time 5.000000 2018-03-06 17:56:29.699 DemoThread[7847:1441602] 主线程:1, 线程名称:<NSThread: 0x6080000794c0>{number = 1, name = main}, object = 210, time 8.000000 2018-03-06 17:56:29.699 DemoThread[7847:1441684] 主线程:0, 线程名称:<NSThread: 0x600000270500>{number = 4, name = (null)}, object = 2210, time 0.000000 2018-03-06 17:56:32.768 DemoThread[7847:1441955] 主线程:0, 线程名称:<NSThread: 0x60800007d480>{number = 3, name = (null)}, object = 2220, time 3.000000 2018-03-06 17:56:38.701 DemoThread[7847:1441602] 主线程:1, 线程名称:<NSThread: 0x6080000794c0>{number = 1, name = main}, object = 220, time 9.0000002、主队列,在主线程中执行// 主队列,在主线程中执行
- (void)runMainQueue
{
// 1 创建队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
[queue addOperationWithBlock:^{
[self showTime:@210];
}];
[queue addOperationWithBlock:^{
[self showTime:@220];
}];
}
- (void)showTime:(id)object
{
// 模拟操作时间耗时
double time = (arc4random() % 10 + 10 / 1000);
[NSThread sleepForTimeInterval:time];
NSLog(@"主线程:%d, 线程名称:%@, object = %@, time %f", [NSThread isMainThread], [NSThread currentThread], object, time);
}
2018-03-06 18:01:30.895 DemoThread[7918:1447374] 主线程:1, 线程名称:<NSThread: 0x608000065080>{number = 1, name = main}, object = 210, time 2.000000
2018-03-06 18:01:35.897 DemoThread[7918:1447374] 主线程:1, 线程名称:<NSThread: 0x608000065080>{number = 1, name = main}, object = 220, time 5.000000
// 主队列,在主线程中执行 - (void)runMainQueue { // 1 创建队列 NSOperationQueue *queue = [NSOperationQueue mainQueue]; // 2 创建任务,不需要调用任务的开始命令start NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@110]; NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@120]; NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{ [self showTime:@220]; }]; [operation3 addExecutionBlock:^{ // 并行,开辟子线程,在子线程中执行 [self showTime:@2210]; }]; // 3 添加到队列中 [queue addOperation:operation1]; [queue addOperation:operation2]; [queue addOperation:operation3]; } - (void)showTime:(id)object { // 模拟操作时间耗时 double time = (arc4random() % 10 + 10 / 1000); [NSThread sleepForTimeInterval:time]; NSLog(@"主线程:%d, 线程名称:%@, object = %@, time %f", [NSThread isMainThread], [NSThread currentThread], object, time); } 2018-03-06 18:03:42.451 DemoThread[7949:1449547] 主线程:1, 线程名称:<NSThread: 0x600000071400>{number = 1, name = main}, object = 110, time 7.000000 2018-03-06 18:03:46.453 DemoThread[7949:1449547] 主线程:1, 线程名称:<NSThread: 0x600000071400>{number = 1, name = main}, object = 120, time 4.000000 2018-03-06 18:03:47.455 DemoThread[7949:1449547] 主线程:1, 线程名称:<NSThread: 0x600000071400>{number = 1, name = main}, object = 220, time 1.000000 2018-03-06 18:03:49.454 DemoThread[7949:1450059] 主线程:0, 线程名称:<NSThread: 0x6080002678c0>{number = 3, name = (null)}, object = 2210, time 3.0000003、其他队列,开辟子线程执行
// 定义变量 NSOperationQueue *queueOther;
// 其他队列,开辟新的子线程 - (void)runOtherQueue { // 1 创建队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.name = @"queue other"; // 优先级 queue.qualityOfService = NSQualityOfServiceDefault; // 最大并发数(-1表示不进行限制,默认为并发执行;1表示串行执行;大于1时,进行并发执行) queue.maxConcurrentOperationCount = 5; // // 2 创建任务,不需要调用任务的开始命令start // NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@110]; // operation1.name = @"operation1"; // NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@120]; // operation2.name = @"operation2"; // NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{ // [self showTime:@220]; // }]; // operation3.name = @"operation3"; // [operation3 addExecutionBlock:^{ // // 并行,开辟子线程,在子线程中执行 // [self showTime:@2210]; // }]; // // 依赖关系 // [operation1 addDependency:operation2]; // [operation3 addDependency:operation1]; // // // 3 添加到队列中 // [queue addOperation:operation1]; // [queue addOperation:operation2]; // [queue addOperation:operation3]; // 或3(省略2) // [queue addOperationWithBlock:^{ // [self showTime:@210]; // }]; // [queue addOperationWithBlock:^{ // [self showTime:@220]; // }]; // 多任务 for (int i = 0; i < 1000; i++) { // NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(showTime:) object:@(i + 1)]; [queue addOperation:operation]; // 或 // [queue addOperationWithBlock:^{ // [self showTime:@(i + 1)]; // }]; } // NSLog(@"start: %ld, %ld", queue.operationCount, queue.operations.count); queueOther = queue; }
- (void)showTime:(id)object { if (((NSNumber *)object).integerValue == 10) { NSInvocationOperation *operation = [queueOther.operations objectAtIndex:9]; [operation cancel]; if ([operation isCancelled]) { NSLog(@"已经退出执行"); return; } } // 模拟操作时间耗时 double time = (arc4random() % 10 + 10 / 1000); [NSThread sleepForTimeInterval:time]; NSLog(@"主线程:%d, 线程名称:%@, object = %@, time %f", [NSThread isMainThread], [NSThread currentThread], object, time); }
2018-03-06 18:05:10.013 DemoThread[7971:1451469] 主线程:0, 线程名称:<NSThread: 0x60000026b040>{number = 3, name = (null)}, object = 1, time 0.000000 2018-03-06 18:05:10.013 DemoThread[7971:1451484] 主线程:0, 线程名称:<NSThread: 0x6080002713c0>{number = 4, name = (null)}, object = 4, time 0.000000 2018-03-06 18:05:10.023 DemoThread[7971:1451407] start: 998, 998 2018-03-06 18:05:16.018 DemoThread[7971:1451587] 主线程:0, 线程名称:<NSThread: 0x60000026b400>{number = 5, name = (null)}, object = 6, time 6.000000 2018-03-06 18:05:17.017 DemoThread[7971:1451484] 主线程:0, 线程名称:<NSThread: 0x6080002713c0>{number = 4, name = (null)}, object = 7, time 7.000000 2018-03-06 18:05:17.018 DemoThread[7971:1451484] 主线程:0, 线程名称:<NSThread: 0x6080002713c0>{number = 4, name = (null)}, object = 9, time 0.000000 2018-03-06 18:05:17.019 DemoThread[7971:1451484] 已经退出执行 2018-03-06 18:05:18.016 DemoThread[7971:1451466] 主线程:0, 线程名称:<NSThread: 0x6080002712c0>{number = 6, name = (null)}, object = 5, time 8.000000 2018-03-06 18:05:18.421 DemoThread[7971:1451407] pause: 993, 993 2018-03-06 18:05:19.016 DemoThread[7971:1451485] 主线程:0, 线程名称:<NSThread: 0x608000461080>{number = 7, name = (null)}, object = 2, time 9.000000 2018-03-06 18:05:19.016 DemoThread[7971:1451467] 主线程:0, 线程名称:<NSThread: 0x608000271080>{number = 8, name = (null)}, object = 3, time 9.000000 2018-03-06 18:05:21.022 DemoThread[7971:1451469] 主线程:0, 线程名称:<NSThread: 0x60000026b040>{number = 3, name = (null)}, object = 8, time 5.000000 2018-03-06 18:05:21.253 DemoThread[7971:1451407] pause: 990, 990 2018-03-06 18:05:22.019 DemoThread[7971:1451466] 主线程:0, 线程名称:<NSThread: 0x6080002712c0>{number = 6, name = (null)}, object = 12, time 4.000000 2018-03-06 18:05:22.917 DemoThread[7971:1451407] cancle: 988, 988
- (void)pauseClick { if (queueOther) { // 暂停或继续 NSLog(@"pause: %ld, %ld", queueOther.operationCount, queueOther.operations.count); queueOther.suspended = !queueOther.isSuspended; } }
- (void)cancleClick { if (queueOther) { // 停止 NSLog(@"cancle: %ld, %ld", queueOther.operationCount, queueOther.operations.count); [queueOther cancelAllOperations]; } }图片下载示例
bool isCancelOperation = NO; - (void)stopClick { if (self.queue) { NSLog(@"1 停止"); isCancelOperation = YES; [self.queue cancelAllOperations]; } }
- (void)showCount:(NSNumber *)number { NSInteger count = arc4random() % 1000; for (int i = 0; i < count; i++) { NSLog(@"第 %@ 个 i = %@", number, @(i)); // 休眠n秒再执行 [NSThread sleepForTimeInterval:0.2]; // 停止 if (isCancelOperation) { NSLog(@"2 停止"); break; } } }
- (void)downloadImage:(NSString *)imageUrl { NSURL *url = [NSURL URLWithString:imageUrl]; NSData *data = [[NSData alloc] initWithContentsOfURL:url]; UIImage *image = [[UIImage alloc] initWithData:data]; if (image == nil) { } else { // [self performSelectorOnMainThread:@selector(updateImage:) withObject:image waitUntilDone:YES]; [self performSelectorInBackground:@selector(updateImage:) withObject:image]; } }
- (void)updateImage:(UIImage *)image { self.imageview.image = image; }
NSString *imageUrl = @"http://ww1.sinaimg.cn/crop.0.0.1242.1242.1024/763fb12bjw8empveq3eq8j20yi0yiwhw.jpg"; NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(downloadImage:) object:imageUrl]; self.queue = [[NSOperationQueue alloc] init]; [self.queue addOperation:operation]; NSInvocationOperation *operationCount1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(showCount:) object:@(11)]; [self.queue addOperation:operationCount1]; NSInvocationOperation *operationCount2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(showCount:) object:@(12)]; [self.queue addOperation:operationCount2];
相关文章推荐
- Python3写爬虫(四)多线程实现数据爬取
- C#实现多线程的同步方法实例分析
- 浅谈chuck-lua中的多线程
- C#简单多线程同步和优先权用法实例
- C#多线程学习之(四)使用线程池进行多线程的自动管理
- C#多线程编程中的锁系统(三)
- 解析C#多线程编程中异步多线程的实现及线程池的使用
- C#多线程学习之(六)互斥对象用法实例
- 基于一个应用程序多线程误用的分析详解
- C#多线程学习之(三)生产者和消费者用法分析
- C#多线程学习之(一)多线程的相关概念分析
- C#多线程之Thread中Thread.IsAlive属性用法分析
- 分享我在工作中遇到的多线程下导致RCW无法释放的问题
- C#多线程编程之使用ReaderWriterLock类实现多用户读与单用户写同步的方法
- C#多线程传递参数及任务用法示例
- C#控制台下测试多线程的方法
- 21天学习android开发教程之SurfaceView与多线程的混搭
- Ruby 多线程的潜力和弱点分析
- C#中WPF使用多线程调用窗体组件的方法
- C#如何对多线程、多任务管理(demo)