多线程之NSOperation详解

2020-06-08  本文已影响0人  Will_iOS程序员

NSOperation简介

NSOperation是开启多线程其中的一种方式,是个抽象类,通常是使用子类NSInvocationOperation和NSBlockOperation。

1、NSOperation用法

1.1、继承

NSOperation不能直接使用,但可以继承NSOperation,通过实现main方法,把要执行的操作放在main里。
代码如下:

#import "CustomOperation.h"

@implementation CustomOperation

- (void)main{
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"0------%@",[NSThread currentThread]);
    }
}
/**
 继承NSOperation,重写main方法,main方法里添加要执行的操作
 在当前线程运行
 */
- (void)customOperation{
    CustomOperation *op = [[CustomOperation alloc] init];
    [op start];
}

输出结果:


image.png

NSOperation 默认情况下都是在当前线程执行
如果要在其他线程执行,如下代码

/**
 在其他线程运行
 */
- (void)runInOtherThread{
    [NSThread detachNewThreadSelector:@selector(customOperation) toTarget:self withObject:nil];
}
image.png

1.2、NSOperation相互依赖

addDependency:相互依赖
removeDependency:移除依赖

/**
 addDependency,相互依赖大于优先级
 */
- (void)operation_addDependency
{
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method2) object:nil];
    NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method3) object:nil];
    
    [op1 addDependency:op2];//op1依赖op2
    [op2 addDependency:op3];//op2依赖op3
//    [op2 removeDependency:op3];移除依赖
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    
    
}
image.png

1.3、NSOperation常用的属性和方法

取消/暂停/恢复操作

2、NSInvocationOperation用法

2.1、NSInvocationOperation不添加queue

在当前线程串行执行操作

/**
 子类:NSInvocationOperation
 知识点:不添加queue,串行执行操作
 */
- (void)InvocationOperation_noneQueue{
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method1) object:nil];
    [invocationOperation start];
}

输出结果:


image.png

3、NSBlockOperation用法

3.1、NSBlockOperation不添加queue

在当前线程串行执行操作

/**
子类:NSBlockOperation
知识点:不添加queue,串行执行操作
*/
- (void)blockOperation_noneQueue{
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    }];
    [blockOperation start];
}

输出结果:


image.png

3.2、NSBlockOperation --addExecutionBlock

添加额外的操作。可能会在不同线程并行(操作多的情况下),由系统决定。

/**
子类:NSBlockOperation
知识点:addExecutionBlock,添加额外的操作,可能会在不同线程并行
 
*/
- (void)blockOperation_addExecutionBlock{
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"blockOperationWithBlock------%@",[NSThread currentThread]);
        }
    }];
    [blockOperation addExecutionBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"addExecutionBlock---1---%@",[NSThread currentThread]);
        }
    }];
    [blockOperation addExecutionBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"addExecutionBlock---2---%@",[NSThread currentThread]);
        }
    }];
    [blockOperation start];
    
    
}

输入结果:


image.png

4、NSOperationQueue用法

4.1、NSOperation开启多线程

NSOperation 添加quque才能开启多线程,不然就是在当前线程运行
代码如下:

- (void)queue_NSInvocationOperation_NSBlockOperation{
    
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method2) object:nil];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        [self method3];
    }];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    
        //警告:不要即把操作添加到操作队列中,又调用操作的start方法,这样是不允许的!否则运行时直接报错。
    //    [op1 start];
    //    [op2 start];
    //    [op3 start];
    
}
- (void)method1{
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"1------%@",[NSThread currentThread]);
    }
}
- (void)method2{
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"2------%@",[NSThread currentThread]);
    }
}
- (void)method3{
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"3------%@",[NSThread currentThread]);
    }
}

输入结果:


image.png

4.2、NSOperationQueue-- addOperationWithBlock

addOperationWithBlock,开启新线程,并发执行

/**
 addOperationWithBlock,开启新线程,并发执行
 */
- (void)queue_addOperationWithBlock
{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 2;
    
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1---%@",[NSThread currentThread]);
        }
    }];
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2---%@",[NSThread currentThread]);
        }
    }];
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3---%@",[NSThread currentThread]);
        }
    }];
    
}
image.png

4.3、NSOperationQueue的一些属性

控制并发数,maxConcurrentOperationCount
当maxConcurrentOperationCount=1是串行,
当maxConcurrentOperationCount>1是并行。

优先级,测试过貌似没啥效果,有问题欢迎大家指正

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

服务质量,iOS8后推出的,测试过貌似没啥效果,有问题欢迎大家指正

typedef NS_ENUM(NSInteger, NSQualityOfService) {
    NSQualityOfServiceUserInteractive = 0x21,
    NSQualityOfServiceUserInitiated = 0x19,
    NSQualityOfServiceUtility = 0x11,
    NSQualityOfServiceBackground = 0x09,
    NSQualityOfServiceDefault = -1
} API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));

4.4、NSOperationQueue线程间通讯

主线程刷新UI

/**
 线程间通讯,主线程刷新ui
 */
- (void)queue_otherqueue_mainqueue{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1---%@",[NSThread currentThread]);
        }
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"主线程刷新UI");
            NSLog(@"2---%@",[NSThread currentThread]);
        }];
        
    }];
}

4.5、监听NSOperationQueue完成情况

用kvo的方式监听queue的operationCount

/**
 kvo监听queue的operationCount,监听queue是否执行完成
 */
- (void)queue_kvo_operationCount{
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method2) object:nil];
    NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(method3) object:nil];
    self.queue = [[NSOperationQueue alloc] init];
    self.queue.maxConcurrentOperationCount = 1;
    [self.queue addOperation:op1];
    [self.queue addOperation:op2];
    [self.queue addOperation:op3];

    [self.queue addObserver:self forKeyPath:@"operationCount" options:(NSKeyValueObservingOptionNew) context:nil];
    
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    NSLog(@"operationCount:%zi",self.queue.operationCount);
}
image.png

4.6、NSOperationQueue常用的属性和方法

取消/暂停/恢复操作

5、 NSOperation、NSOperationQueue 非线程安全线程安全

NSOperation是非线程安全的

线程安全解决方案:可以给线程加锁,在一个线程执行该操作的时候,不允许其他线程进行操作。iOS 实现线程加锁有很多种方式。@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock、atomic(property) set/ge等等各种方式。这里我们使用 NSLock 对象来解决线程同步问题。NSLock 对象可以通过进入锁时调用 lock 方法,解锁时调用 unlock 方法来保证线程安全。

#pragma mark - 线程不安全
/**
 线程不安全
 */
- (void)ticketSale_unsafe{
    self.totalTicketCount = 50;
    
    NSBlockOperation *salewin1 = [NSBlockOperation blockOperationWithBlock:^{
        [self ticketBeginSale];
        
    }];
    NSBlockOperation *salewin2 = [NSBlockOperation blockOperationWithBlock:^{
        [self ticketBeginSale];
        
    }];
    
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:salewin1];
    [queue addOperation:salewin2];
    
    
}
- (void)ticketBeginSale{
    
    while (1) {
        [self.lock lock];//加锁
        if (self.totalTicketCount > 0) {
            self.totalTicketCount--;
            NSLog(@"剩余票数:%zi",self.totalTicketCount);
            [NSThread sleepForTimeInterval:0.2];
           
        }
        [self.lock unlock];//解锁
        if (self.totalTicketCount <= 0) {
            NSLog(@"票已售完~~~~~~~~");
            
            break;
        }
        
    }
}
image.png

demo源码地址:https://github.com/willcai/NSOperation

上一篇 下一篇

猜你喜欢

热点阅读