ios开发那些事工作生活 Objective - C 开发那些事

深入浅出iOS多线程(四)——NSOperation多线程

2019-07-03  本文已影响47人  struggle3g

深入浅出iOS多线程(一)——线程的概念
深入浅出iOS多线程(二)——pthraed和NSThread的使用
深入浅出iOS多线程(三)——GCD多线程
深入浅出iOS多线程(四)——NSOperation多线程
深入浅出iOS多线程(五)——多线程锁

NSOperation的作用

简介

和GCD一样,NSOperation也是并发编程技术,NSOperation是苹果提供给我们的一套多线程解决方案。实际上NSOperation是基于GCD更高一层的封装,比GCD更加简单,更加方便

NSoperation需要配合NSOperationQueue来实现多线程,NSOperation单独使用时系统同步执行操作,并没有开辟新的线程的能力,只有配合NSoperationQueue才能实现异步执行。

NSOperation是苹果大力推荐的"并发"技术
NSOperation的核心概念是,将"操作"添加进"队列"
GCD将"任务"添加到"队列"

由于NSOperation是基于GCD的,使用步骤也和GCD差不多,其中,NSOperation相当于GCD中的任务,而NSOperationQueue则相当于GCD中的队列,NSOperation实现多线程的使用步骤分3步:

系统会自动取出"队列"中的"操作"执行。

NSOperation多线程的基本使用

NSInvocationOperation的基本使用

根据上述代码我们可以确信NSOperationQueue是一个队列,而NSInvocationOperation是一个操作(任务),那么这个队列到底是什么样的队列,而这个操作是什么样的操作,在上述代码中队列添加更多的"操作",代码如下:

//队列
NSOperationQueue * q = [[NSOperationQueue alloc]init];
    
for (int i = 0; i<10; i++) {
    NSInvocationOperation * op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@(i)];
    //将操作添加到队列 - 会自动异步执行调度方法
    [q addOperation:op];
}

打印结果:

<NSThread: 0x6000016c8cc0>{number = 4, name = (null)}  1
<NSThread: 0x6000016f4140>{number = 3, name = (null)}  0
<NSThread: 0x6000016c0a00>{number = 6, name = (null)}  3
<NSThread: 0x6000016cc380>{number = 5, name = (null)}  2
<NSThread: 0x6000016f4280>{number = 7, name = (null)}  4
<NSThread: 0x6000016f4140>{number = 3, name = (null)}  8
<NSThread: 0x6000016cc380>{number = 5, name = (null)}  6
<NSThread: 0x6000016c0a00>{number = 6, name = (null)}  7
<NSThread: 0x6000016c8cc0>{number = 4, name = (null)}  5
<NSThread: 0x6000016f4280>{number = 7, name = (null)}  9

从打印结果上面看,线程不一样,执行顺序也不一样,说明这个队列是并发队列,队列中的操作是异步操作。

NSBlockOperation的基本使用

NSBlockOperationNSInvocationOperation使用步骤几乎一摸一样,不一样的地方在于,NSBlockOperationNSInvocationOperation省略了一个@selector,代码更加简洁。


/**
    第一种block形式
**/
//1.队列
NSOperationQueue * q = [[NSOperationQueue alloc]init];
    
//2.操作
for (int i = 0; i<10; i++) {
    NSBlockOperation * op = [NSBlockOperation blockOperationWithBlock:^{
         [NSThread currentThread].name = [@(i) stringValue];
        NSLog(@"%@",[NSThread currentThread]);
    }];
    //将操作添加到队列 - 会自动异步执行调度方法
    [q addOperation:op];
}

/**
    第一种block形式
**/
//1.队列
NSOperationQueue * q = [[NSOperationQueue alloc]init];
    
//2.操作
for (int i = 0; i<10; i++) {
    //将操作添加到队列 - 会自动异步执行调度方法
    [q addOperationWithBlock:^{
         [NSThread currentThread].name = [@(i) stringValue];
        NSLog(@"%@",[NSThread currentThread]);
    }];
}

打印结果:

<NSThread: 0x6000001e1900>{number = 5, name = 1}
<NSThread: 0x6000001e1940>{number = 6, name = 3}
<NSThread: 0x6000001ea900>{number = 3, name = 0}
<NSThread: 0x6000001e16c0>{number = 4, name = 2}
<NSThread: 0x6000001ea900>{number = 3, name = 5}
<NSThread: 0x6000001e1940>{number = 6, name = 4}
<NSThread: 0x6000001e1900>{number = 5, name = 6}
<NSThread: 0x6000001e16c0>{number = 4, name = 7}
<NSThread: 0x6000001eaf40>{number = 7, name = 8}
<NSThread: 0x6000001ea900>{number = 3, name = 9}

上述代码运行结果,同样可以知道这个队列是并发队列,队列中的操作是异步操作。

上述代码,所有的代码都要添加一个队列怎么办?

@interface ViewController ()
@property (nonatomic,strong)NSOperationQueue * MyQueue;
@end

- (NSOperationQueue *)MyQueue{
    
    if(!_MyQueue){
        _MyQueue = [[NSOperationQueue alloc]init];
    }
    return _MyQueue;
}

for (int i = 0; i<10; i++) {
    //将操作添加到队列 - 会自动异步执行调度方法
    [self.MyQueue addOperationWithBlock:^{
        [NSThread currentThread].name = [@(i) stringValue];
        NSLog(@"%@",[NSThread currentThread]);
    }];
}

很简洁很爽

NSOperation线程间的通信

//小清新
[self.MyQueue addOperationWithBlock:^{
    NSLog(@"耗时操作");
    [[NSOperationQueue mainQueue]addOperationWithBlock:^{
        NSLog(@"更新UI");
    }];
}];

NSOperation最大并发数

//添加操作进队列
/*
 从 iOS 8.0 开始,无论使用 GCD还是 NSOperation ,都会开启很多线程
 在 iOS 7.0 以前,GCD 通常只会开启 5  6条线程!
 目前线程多了说明:
 1.底层的现场池更大了,能够拿到的线程资源多了!
 2.多控制同时并发的现场数,要求就更高了!
 */
    
for (int i = 0;i < 20; i++) {
    [self.MyQueue addOperationWithBlock:^{
        NSLog(@"%@---%d",[NSThread currentThread],i);
    }];
}

打印结果:

<NSThread: 0x600003e984c0>{number = 4, name = (null)}---1
<NSThread: 0x600003e98540>{number = 5, name = (null)}---2
<NSThread: 0x600003e96980>{number = 6, name = (null)}---3
<NSThread: 0x600003e9c6c0>{number = 3, name = (null)}---0
<NSThread: 0x600003e984c0>{number = 4, name = (null)}---4
<NSThread: 0x600003e96980>{number = 6, name = (null)}---5
<NSThread: 0x600003e98540>{number = 5, name = (null)}---6
<NSThread: 0x600003e9c6c0>{number = 3, name = (null)}---7
<NSThread: 0x600003e984c0>{number = 4, name = (null)}---8
<NSThread: 0x600003e96980>{number = 6, name = (null)}---9
<NSThread: 0x600003e98540>{number = 5, name = (null)}---10
<NSThread: 0x600003e9e280>{number = 7, name = (null)}---11
<NSThread: 0x600003e8f080>{number = 8, name = (null)}---12
<NSThread: 0x600003e8f280>{number = 9, name = (null)}---13
<NSThread: 0x600003e9c6c0>{number = 3, name = (null)}---14
<NSThread: 0x600003e984c0>{number = 4, name = (null)}---15
<NSThread: 0x600003e98540>{number = 5, name = (null)}---16
<NSThread: 0x600003e9e700>{number = 10, name = (null)}---17
<NSThread: 0x600003e9e740>{number = 11, name = (null)}---18
<NSThread: 0x600003e96980>{number = 6, name = (null)}---19

打印结果:

<NSThread: 0x6000002d4f40>{number = 4, name = (null)}---0
<NSThread: 0x6000002ad700>{number = 3, name = (null)}---1
<NSThread: 0x6000002d9ec0>{number = 6, name = (null)}---2
<NSThread: 0x6000002d5000>{number = 5, name = (null)}---3
<NSThread: 0x6000002d5000>{number = 5, name = (null)}---5
<NSThread: 0x6000002ad700>{number = 3, name = (null)}---4
<NSThread: 0x6000002ad700>{number = 3, name = (null)}---7
<NSThread: 0x6000002d9ec0>{number = 6, name = (null)}---6
<NSThread: 0x6000002d9ec0>{number = 6, name = (null)}---9
<NSThread: 0x6000002d4f40>{number = 4, name = (null)}---8
<NSThread: 0x6000002d9ec0>{number = 6, name = (null)}---10
<NSThread: 0x6000002d4f40>{number = 4, name = (null)}---11
<NSThread: 0x6000002d9ec0>{number = 6, name = (null)}---13
<NSThread: 0x6000002ad700>{number = 3, name = (null)}---12
<NSThread: 0x6000002ad700>{number = 3, name = (null)}---15
<NSThread: 0x6000002d4f40>{number = 4, name = (null)}---14
<NSThread: 0x6000002d5000>{number = 5, name = (null)}---16
<NSThread: 0x6000002ad700>{number = 3, name = (null)}---17
<NSThread: 0x6000002ad700>{number = 3, name = (null)}---19
<NSThread: 0x6000002d4f40>{number = 4, name = (null)}---18

为什么实际当中打印会多出几个线程?

  1. 线程中的任务完成以后会回收线程
  2. 当一个任务完成需要从队列中取新的任务,取一个空间的线程,或者是开辟新线程的时候,而1中的线程正在回收,所以这个任务开辟的线程会比原先的线程number+1

NSOperationQueue的属性和方法

suspended

operationCount

可以拿到队列中的操作数

NSLog(@"%tu",self.MyQueue.operationCount);

取消NSOperationQueue中的所有操作

注意:

  1. 队列挂起的时候,取消队列所有操作,不会清空队列的operationCoount只有在队列继续的时候才能清空
  2. 正在执行的操作也不会被清空,也不会被取消
[self.MyQueue cancelAllOperations];

NSOperation依赖关系(Dependency)

waitUntilFinished中的YES会卡住当前线程

[self.MyQueue addOperations:@[op1,op2,op3] waitUntilFinished:YES];

NSOperation依赖关系:

- (void)addDependency:(NSOperation *)op;
- (void)removeDependency:(NSOperation *)op;

例子:

/*
 *  例子:下载/解压/通知用户
 **/
    
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"下载----%@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"解压----%@",[NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"通知用户----%@",[NSThread currentThread]);
}];
    
//NSOperation 提供了依赖关系
//注意:不要指定循环依赖,队列就不工作了,不会造成死锁。
[op2 addDependency:op1];
[op3 addDependency:op2];
    
//YES 会卡住当前线程
[self.MyQueue addOperations:@[op1,op2,op3] waitUntilFinished:YES];
NSLog(@"come here  %@",[NSThread currentThread]);
    
    //主线程通知用户
[[NSOperationQueue mainQueue]addOperation:op3];
    

打印结果:

下载----<NSThread: 0x600002dc3240>{number = 3, name = (null)}
解压----<NSThread: 0x600002dc5440>{number = 4, name = (null)}
通知用户----<NSThread: 0x600002dc3240>{number = 3, name = (null)}
come here  <NSThread: 0x600002d9cb00>{number = 1, name = main}

总结

GCD 和 NSOperation 对比

GCD 在 iOS 4.0 推出,主要针对多核处理器做了优化的并发技术,是C语言的

NSOperation 在 iOS 2.0 推出的,苹果推出 GCD以后,对NSOperation 底层做了重写!

多线程NSOperation结构图

NSOperation.png
上一篇 下一篇

猜你喜欢

热点阅读