NSOperation、NSOperationQueue

2019-08-01  本文已影响0人  眷卿三世

NSOperation、NSOperationQueue学习

简介:

为什么要使用 NSOperation、NSOperationQueue?:

概念:

操作(Operation):

操作队列:

NSOperation:

NSInvocationOperation

/**
 * 使用子类 NSInvocationOperation
 */
- (void)useInvocationOperation {

    // 1.创建 NSInvocationOperation 对象
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];

    // 2.调用 start 方法开始执行操作
    [op start];
}

/**
 * 任务1
 */
- (void)task1 {
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
    }
}

结果:
在不使用NSOperationQueue的情况下,单独使用NSInvocationOperation不会创建新线程,使用的是当前线程处理所有操作。
在其他线程单独调用NSInvocationOperation,不会开启新线程,使用其他线程来完成任务操作。

NSBlockOperation

当单独使用blockOperationWithBlock时,任务只会使用当前线程,不会开启新线程处理任务。
如果想开启新线程执行并行任务,需要结合是使用addExecutionBlock方法来实现。代码如下:

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
             NSLog(@"1---%@", [NSThread currentThread]);
        }
    }];
    
    [op addExecutionBlock:^{
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
             NSLog(@"2---%@", [NSThread currentThread]);
        }
    }];
    
    [op addExecutionBlock:^{
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
             NSLog(@"3---%@", [NSThread currentThread]);
        }
    }];
    
    [op addExecutionBlock:^{
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
             NSLog(@"4---%@", [NSThread currentThread]);
        }
    }];
    
    [op start];

NSOperationQueue

1、创建NSBlockOperation、NSInvocationOperation任务操作,并加入到队列中之后,可以开启新的线程,并发执行。

2、使用addOperationWithBlock加入队列中的操作也能够开启新的线程进行并发执行。

控制并发和串行:

注意:maxConcurrentOperationCount 控制的不是并发线程的数量,而是一个队列中同时能并发执行的最大操作数。而且一个操作也并非只能在一个线程中运行。

优先级

NSOperation提供了queuePriority(优先级)属性,queuePriority属性适用于同一操作队列中的操作,不适用于不同操作队列的操作。默认情况下,所有新创建的操作对象优先级都是NSOperationQueuePriorityNormal,但是我们可以通过setQueuePriority:方法来改变当前操作在同一队列中的执行优先级。

// 优先级的取值
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

在优先级中,需要注意的点:
1、优先级不能取代依赖关系,也就是在有依赖关系的时候,以依赖为先,其次是优先级。
2、在队列中就绪的操作,优先级高的先执行。

线程间通信

在 iOS 开发过程中,我们一般在主线程里边进行 UI 刷新,例如:点击、滚动、拖拽等事件。我们通常把一些耗时的操作放在其他线程,比如说图片下载、文件上传等耗时操作。而当我们有时候在其他线程完成了耗时操作时,需要回到主线程,那么就用到了线程之间的通讯。

// 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 2.添加操作
    [queue addOperationWithBlock:^{
        // 异步进行耗时操作
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
        }
        
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            // 进行一些 UI 刷新等操作
            for (int i = 0; i < 2; i++) {
                [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
                NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
            }
        }];
    }];

安全:

当多个线程同时有写操作,同时操作同一个全局变量时,有安全问题。
解决:同步加锁

- (void)initTicketStatusSave {
    NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
    
    self.ticketSurplusCount = 50;
    self.lock = [[NSLock alloc] init];
    
    NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
    
    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        [self saleTicketSafe];
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        [self saleTicketSafe];
    }];
    
    // 5.添加操作,开始卖票
    [queue1 addOperation:op1];
    [queue2 addOperation:op2];
}

- (void)saleTicketSafe {
    while (1) {
        // 加锁
        [self.lock lock];
        
        if (self.ticketSurplusCount > 0) {
            // 如果还有票,继续售卖
            self.ticketSurplusCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", (long)self.ticketSurplusCount, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:.2];
        }
        
        [self.lock unlock];
        
        if (self.ticketSurplusCount <= 0) {
            NSLog(@"所有火车票均已售完");
            break;
        }
    }
}

iOS 实现线程加锁有很多种方式。@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock等等各种方式。

常用属性和方法归纳

NSOperation 常用属性和方法

1、取消操作方法

NSOperationQueue 常用属性和方法

1、取消/暂停/恢复操作

注意:
1、这里的暂停和取消(包括操作的取消和队列的取消)并不代表可以将当前的操作立即取消,而是当当前的操作执行完毕之后不再执行新的操作。
2、暂停和取消的区别就在于:暂停操作之后还可以恢复操作,继续向下执行;而取消操作之后,所有的操作就清空了,无法再接着执行剩下的操作。

上一篇下一篇

猜你喜欢

热点阅读