【iOS】NSOperation,NSOperationQueue

news2024/10/5 14:17:37

文章目录

  • 前言
  • 概念
    • 使用NSOperation,NSoperationQueue的好处
    • 操作和操作队列
      • 操作(Operation)
      • 操作队列(Operation Queues)
    • NSOperation,NSOperationQueue常用属性和方法归纳
      • NSOperation常用属性和方法
      • NSOperationQueue的常用属性和方法
    • 暂停和取消
  • 使用步骤
  • 基本使用
    • 创建操作
      • 使用子类NSInvocationOperation
      • 使用子类NSBlockOperation
      • 使用继承自NSOperation自定义子类
        • 重写main方法
        • 重写start方法
    • 创建队列
    • 将操作加入队列中
    • NSOperationQueue控制串行执行、并行执行
    • NSOperation操作依赖
    • NSOperation优先级
      • queuePriority:
    • NSOperation、NSOperationQueue线程间的通信
  • 多线程的比较
      • pthread
      • GCD与NSOperation
      • GCD 与 NSThread 的区别
      • 多线程的优缺点


前言

提示:这里可以添加本文要记录的大概内容:

例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

概念

NSOperation和NSOperationQueue是苹果提供给我们的一套多线程解决方案,是基于GCD更高一层的封装,完全面向对象。但是比GCD更加简单,代码可读性更高

使用NSOperation,NSoperationQueue的好处

  • 可添加的代码块,在操作完成之后执行NSOperation的completionBlock属性
  • 添加操作之间的依赖关系,方便控制执行顺序
  • 设定操作的优先级
  • 可以很方便的取消有关操作的执行
  • 使用KVO观察对操作状态的更改executing,finished,cancelled通过KVO的方式移除finished值为YES的NSOperation
  • 可支持最大并发操作数,来控制串行,并发

操作和操作队列

NSOperation是基于GCD的更高一层的封装,GCD中的一些概念同样适用于NSOperation、NSOperationQueue。
在NSOperation、NSOperationQueue中也有类似任务(操作)、队列(操作队列)的概念

操作(Operation)

  • 执行操作的意思,就是在线程中执行的那段代码
  • 在GCD中是放在Block中的。在NSOperation中,我们使用NSOperation子类SNInvacationOperation,NSBlockOperation,或者是自定义子类来封装。

操作队列(Operation Queues)

  • 顾名思义,显而易见操作队列是用来存放操作的队列。不同于GCD中的调度队列FIFO(先进先出)的原则,NSOperationQueue对于添加到队列中的操作,首先进入准备就绪的状态,(就绪状态取决于与操作之间的依赖关系),然后进入就绪状态的这些操作,开始执行的顺序由操作之间的优先级决定。(优先级是操作对象自身的属性)

NSOperation,NSOperationQueue常用属性和方法归纳

NSOperation常用属性和方法

  • 取消操作cancel
  • 判断操作状态方法
    • isFinished判断操作是否已经结束
    • isCancelled判断操作是否取消
    • is Excuting判断操作是否正在运行
    • isReady判断是否处于就绪状态
    • isAsynchronous表示任务是并发还是同步执行
  • 操作同步
    • waitUntilFinished阻塞当前线程
    • setCompletionBlock:(void(^)(void))block;当前操作完毕之后执行block
    • addDependency添加依赖
    • removeDependency移除依赖
    • @property (readonly, copy) NSArray<NSOperation *> *dependencies;数组存储操作

NSOperationQueue的常用属性和方法

  • 取消/暂停/恢复队列中的操作
    • cancelAllOperations;取消队列中所有的操作
    • isSuspended判断队列是否处理暂停状态 YES:暂停状态,NO恢复状态
    • setSuspended:(BOOL)b;设置操作的暂停和恢复 YES:暂停,NO:恢复
  • 同步操作
    • waitUntilAllOperationsAreFinished;阻塞当前线程,直到队列中的操作全部完成
  • 添加/获取
    • addOperationWithBlock:(void (^)(void))block向队列中添加一个NSBlockOperation类型的操作对象
    • addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait;添加操作数组,wait标志是否阻塞当前线程知道所有操作结束
    • operations当前在队列中的操作数组
    • operationCount操作个数
  • 获取队列
    • currentQueue当前队列,如果当前线程不在Queue上,返回nil
    • mainQueue获取主队列

暂停和取消

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

使用步骤

  • 创建操作:将需要执行的操作放到一个NSOperation对象中。
  • 创建对象:创建NSOperationQueue对象
  • 将操作加入到队列中:将NSOperation对象添加到NSOperationQueue队列中。

系统会自动将NSOperationQueue队列中的NSOperation对象取出来在新线程中操作。

基本使用

创建操作

NSOperation是一个抽象类,不能自己创建实例,我们通常使用它的子类进行封装操作。
使用子类NSInvocationOperaation
使用子类NSBlockOperation
自定义继承自NSOperation的子类,通过实现内部的相应方法来封装操作

使用子类NSInvocationOperation

请添加图片描述

- (void)viewDidLoad {
    [super viewDidLoad];
    // 1.创建 NSInvocationOperation 对象
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(testOp) object:nil];
    // 2.调用 start 方法开始执行操作
    [op start];

    // Do any additional setup after loading the view.
}

- (void)testOp {
    NSLog(@"testOp--%@", [NSThread currentThread]);
}

没有使用NSOperationQueue,并且我们是在当前线程(主线程)中执行有关操作,所以他是在当前线程(主线程)中完成操作的,没有开启新线程。
如果在其他线程中执行操作,则打印的结果为其他线程。总之,它是在哪个线程创建并启动的,他就会在哪个线程执行。

使用子类NSBlockOperation

//创NSBlockOperation对象
    NSBlockOperation* op = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blockOp---%@", [NSThread currentThread]);
    }];
    //调用start方法开始执行
    [op start];

请添加图片描述

这个其实就和上边NSInvocationOperation一样,只是使用block之后更加简洁了,也没有开启新线程,哪个线程创建并执行就是那个线程

NSBlockOperation还有一个方法addExecutionBlock:,通过addExecutionBlock:就可以为NSBlockOperation添加额外的操作。这些操作(包括blockOperationWithBlock中的操作)可以在不同的线程中同时(并发)执行。只有当所有相关的操作已经完成执行时,才视为完成

如果添加的操作多的话,blockOperationWithBlock:中的操作也有可能会在其他线程(非当前线程)中执行,这是由系统决定的,并不是说添加到blockOperationWithBlock:中的操作一定在当前线程中执行

NSBlockOperation* op = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op```%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"1---block  %@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"2---block %@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"3---block %@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"4---block %@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"5---block %@", [NSThread currentThread]);
    }];
    [op start];

请添加图片描述

使用子类NSBlockOperation,并调用addExecutionBlock:的情况下,blockOperationWithBlock:方法中的操作和额外加的操作是在不同线程中异步执行的。同时,额外操作多的时候,blockOperationWithBlock:方法中的操作有可能不会在当前线程中执行
开启的线程数是由系统来决定的

注意:addExecutionBlock: 方法必须在start()方法之前执行,否则就会报错。

使用继承自NSOperation自定义子类

我们可以通过自定义继承自NSOperation的子类,重写main或者start来定义自己的NSOperation对象。

如果只是重写了main方法,有底层控制变更任务执行、完成状态以及任务退出。

如果重写了start方法,需要自己控制任务状态。

重写main方法比较简单,我们不需要管理线程的状态属性executing(是否正在执行)和finished(是否完成)。当main执行完返回的时候,这个操作就结束了。

重写main方法

//  NSOperationTest.h
//  NSOPeration
//
//  Created by 王璐 on 2023/5/5.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSOperationTest : NSOperation

@end

NS_ASSUME_NONNULL_END

//  NSOperationTest.m
//  NSOPeration
//
//  Created by 王璐 on 2023/5/5.
//

#import "NSOperationTest.h"

@implementation NSOperationTest
- (void)main {
    if (!self.isCancelled) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"test1---%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
        NSLog(@"test2---%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
        NSLog(@"test3---%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
        NSLog(@"test4---%@", [NSThread currentThread]);
    }
}
@end


NSOperationTest* test = [[NSOperationTest alloc] init];
    NSLog(@"%d", test.finished);
    [test start];
    NSLog(@"%d", test.finished);

请添加图片描述

重写start方法

- (void)start {
    if (!self.isCancelled) {
        
        [NSThread sleepForTimeInterval:2];
        NSLog(@"test1---%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
        NSLog(@"test2---%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
        NSLog(@"test3---%@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
        NSLog(@"test4---%@", [NSThread currentThread]);
    }
}

请添加图片描述
我们明显能看到,重写start方法系统没有自动管理线程的状态属性。
我们没有使用NSOperationQueue,在主线程中创建自定义子类的操作对象并执行,并没有开启新线程。

创建队列

NSOperationQueue共有两种队列:主队列、自定义队列,其中自定义队列同时包含了串行、并发功能

  • 主队列:凡是添加到主队列中的操作,都会放到主线程中执行。
  • 自定义队列(非主队列):添加到这种队列中的操作,会自动放到子线程中执行,同时包含了 串行、并发 功能。

    //主队列获取方法
    NSOperationQueue* queue = [NSOperationQueue mainQueue];
    //自定义队列(非主队列)
    NSOperationQueue* queue = [[NSOperationQueue alloc] init];

将操作加入队列中

NSOperation需要配合NSOperationQueue才能实现多线程,将创建好的操作加入到队列中,有两种方法:

  • (void)addOperation:(NSOperation *)op;方法
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation* first = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationFirst) object:nil];
    NSBlockOperation* second = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2---%@", [NSThread currentThread]);
    }];
    [second addExecutionBlock:^{
        NSLog(@"add---%@", [NSThread currentThread]);
    }];
    NSInvocationOperation* third = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationThird) object:nil];
    [queue addOperation:first];
    [queue addOperation:second];
    [queue addOperation:third];



- (void)operationFirst {
    NSLog(@"1---%@", [NSThread currentThread]);
}
- (void)operationThird {
    NSLog(@"3---%@", [NSThread currentThread]);
}

请添加图片描述
我们可以看到,添加到队列中的操作开启了新线程来完成。

  • (void)addOperationWithBlock:(void (^)(void)block);方法

不用创建操作,直接在block中添加操作,将block加入到队列中:

[queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"1---%@", [NSThread currentThread]);
    }];
    
    [queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"2---%@", [NSThread currentThread]);
    }];
    
    [queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"3---%@", [NSThread currentThread]);
    }];

请添加图片描述
与上述方法相同,不过使用block看起来更简洁了。
但是,我们看上面的例子,基本上都是并行执行的,下面就来说说怎么控制并发和串行

NSOperationQueue控制串行执行、并行执行

操作队列有一个属性,最大并发操作数,用来控制一个特定的队列中可以有多少个操作同时并发执行,也就是一个队列中同时能并发执行的最大操作数:

@property NSInteger maxConcurrentOperationCount;

maxConcurrentOperationCount默认为-1,表示不进行限制,可进行并发执行
maxConcurrentOperationCount为1时,此时队列为串行队列,只能串行执行
maxConcurrentOperationCount大于1时,队列为并发队列。操作并发执行。当这个值超过了系统限制,就会自动调整为系统设定的值。

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

//queue.maxConcurrentOperationCount = 1;
queue.maxConcurrentOperationCount = 2;

[queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"1---%@", [NSThread currentThread]);
}];

[queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"2---%@", [NSThread currentThread]);
}];

[queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"3---%@", [NSThread currentThread]);
}];

[queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"4---%@", [NSThread currentThread]);
}];

[queue addOperationWithBlock:^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"5---%@", [NSThread currentThread]);
}];

请添加图片描述
请添加图片描述
对于开启线程数,是由系统决定的,不需要我们来管理。

NSOperation操作依赖

NSOperation、NSOperationQueue最吸引人的就是它能够添加操作之间的依赖关系,通过依赖关系,我们就可以很方便的控制操作之间的执行先后顺序。

NSOperation提供了3个接口供我们使用依赖

  • (void)addDependency:(NSOperation *)op添加依赖,是当前操作依赖op的完成,op完成之后才会执行当前操作。
  • (void)removeDependency:(NSOperation *)op移除依赖,取消当前操作对操作op的依赖。
    @property (readonly, copy) NSArray<NSOperation *> *dependencies;操作对象的一个属性,在当前操作开始执行之前完成执行的所有操作对象数组。

没有添加依赖时:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
NSBlockOperation *firstOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"firstOperation");
}];
NSBlockOperation *secondOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"secondOperation");
}];
NSBlockOperation *thirdOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"thirdOperation");
}];

[queue addOperation:firstOperation];
[queue addOperation:secondOperation];
[queue addOperation:thirdOperation];

在这里插入图片描述
没有添加依赖,执行都是并发执行的,没有次序可言。
添加依赖后:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSBlockOperation *firstOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"firstOperation");
}];
NSBlockOperation *secondOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"secondOperation");
}];
NSBlockOperation *thirdOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"thirdOperation");
}];

[secondOperation addDependency:firstOperation]; // 让secondOperation依赖于firstOperation,即firstOperation先执行,在执行secondOperation
[thirdOperation addDependency:secondOperation]; // 让thirdOperation依赖于secondOperation,即secondOperation先执行,在执行thirdOperation

[queue addOperation:firstOperation];
[queue addOperation:secondOperation];
[queue addOperation:thirdOperation];

请添加图片描述
相互依赖:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
NSBlockOperation *firstOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"firstOperation");
}];
NSBlockOperation *secondOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"secondOperation");
}];
NSBlockOperation *thirdOperation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"thirdOperation");
}];

[secondOperation addDependency:firstOperation]; // 让secondOperation依赖于firstOperation,即firstOperation先执行,在执行secondOperation
[firstOperation addDependency:secondOperation]; // 让firstOperation依赖于secondOperation,即secondOperation先执行,在执行firstOperation

[queue addOperation:firstOperation];
[queue addOperation:secondOperation];
[queue addOperation:thirdOperation];

请添加图片描述
在添加相互依赖之后,这两个都不执行了。

NSOperation优先级

依赖只是一种执行关系罢了,NSOperation还为我们专门提供了优先级属性,我们可以通过setQueuePriority:方法来设置同一队列中操作的优先级,下面是系统给定的优先级(默认为NSOperationQueuePriorityNormal):

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

对于添加到队列中的操作,首先进入准备就绪的状态,然后进入就绪状态的操作的开始执行顺序由操作之间的相对的优先级决定。

就绪状态取决于操作之间的依赖关系,也就是只有这个操作的依赖操作完成了,该操作才会处于就绪状态。

举个例子:
当有四个优先级都是NSOperationQueuePriorityNormal(默认优先级)的操作:op1、op2、op3、op4,op2依赖于op3,op3依赖于op4。

其中只有op1、op4没有需要依赖的操作,所以op1、op4就是处于准备就绪状态的操作。
op2、op3都有依赖的操作,所以op2、op3都不是准备就绪的操作。
当op4完成时,op3进入就绪状态;当op3完成时,op2进入就绪状态

queuePriority:

  • queuePriority属性决定了已进入就绪状态下的操作之间的开始执行顺序。优先级不能取代依赖关系,该属性仅决定开始执行顺序,并不能保证完成执行顺序。
  • 如果一个队列中既包含高优先级操作、也有低优先级操作,并且这两个操作都已经准备就绪,那么队列就会先执行高优先级操作。
  • queuePriority属性决定的是进入就绪状态下的操作之间的开始执行顺序,并不保证执行完成顺序。而依赖则是控制两个操作之间的执行顺序,使一个操作在它依赖的操作执行完成之后再开始执行。
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    NSBlockOperation *firstOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"begin firstOperation");
        [NSThread sleepForTimeInterval:2];
        NSLog(@"firstOperation end");
    }];
    firstOperation.queuePriority = NSOperationQueuePriorityLow;

    NSBlockOperation *secondOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"begin secondOperation");
        [NSThread sleepForTimeInterval:2];
        NSLog(@"secondOperation end");
    }];
    secondOperation.queuePriority = NSOperationQueuePriorityHigh;

    NSBlockOperation *thirdOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"begin thirdOperation");
        [NSThread sleepForTimeInterval:2];
        NSLog(@"thirdOperation end");
    }];
    thirdOperation.queuePriority = NSOperationQueuePriorityNormal;

    queue.maxConcurrentOperationCount = 3;


    [queue addOperation:firstOperation];
    [queue addOperation:secondOperation];
    [queue addOperation:thirdOperation];

请添加图片描述

  • 如果我们将最大操作执行数设置为1,那么队列中操作数将会按添加的顺序串行执行,一个操作执行完才会执行另一个操作。
  • 如果队列中所有的操作的优先级相同,并且也进入就绪状态,那么执行的顺序就按照提交到队列的顺序执行。否则,队列总是执行相对于其他就绪操作优先级更高的操作。

问题:为什么我们设置了优先级,他还是不会按照优先级大小来执行操作?
可以通过NSOperation 的queuePriority属性来设置操作在队列中的执行优先级:

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

@property NSOperationQueuePriority queuePriority;

可以通过NSOperation 的qualityOfService属性来设置操作在队列中的服务质量(iOS8 以后苹果推荐使用服务质量替代优先级):

typedef NS_ENUM(NSInteger, NSQualityOfService) {
    NSQualityOfServiceUserInteractive = 0x21,
    NSQualityOfServiceUserInitiated = 0x19,
    NSQualityOfServiceUtility = 0x11,
    NSQualityOfServiceBackground = 0x09,
    NSQualityOfServiceDefault = -1
} 

@property NSQualityOfService qualityOfService;

NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"执行任务1,%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"执行任务2,%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"执行任务3,%@",[NSThread currentThread]);
    }];
    // 设置优先级
    op1.queuePriority = NSOperationQueuePriorityLow;
    op2.queuePriority = NSOperationQueuePriorityHigh;
    op3.queuePriority = NSOperationQueuePriorityNormal;
    // 将操作加入队列
    [queue addOperations:[NSArray arrayWithObjects:op1, op2, op3, nil] waitUntilFinished:YES];

请添加图片描述

NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"执行任务1,%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"执行任务2,%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"执行任务3,%@",[NSThread currentThread]);
    }];
    // 设置服务质量
    op1.qualityOfService = NSQualityOfServiceBackground;
    op2.qualityOfService = NSQualityOfServiceUserInitiated;
    op3.qualityOfService = NSQualityOfServiceUtility;
    // 将操作加入队列
    [queue addOperations:[NSArray arrayWithObjects:op1, op2, op3, nil] waitUntilFinished:YES];

请添加图片描述
优先级和服务质量都不能绝对保证代码的优先执行情况,这就关系到优先级反转了,可以自己下去了解一下。
总之,设置优先级和服务质量都不能保证代码执行顺序的绝对性,只是改变其先执行或后执行的概率。
如果我们要确保操作的执行的先后顺序,即操作间有同步关系,我们应该使用依赖关系来确保绝对的执行顺序,即使操作对象位于不同的操作队列中。在操作对象的所有依赖操作完成执行之前,操作对象不会被视为已准备好执行。

NSOperation、NSOperationQueue线程间的通信

在iOS开发过程中,我们一般要在主线程中进行UI刷新,通常把一些耗时的操作放在其他线程中,当其他线程完成了耗时操作时,需要回到主线程,那么就用到了线程间的通信:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
    for (int i = 0; i < 2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"%d--%@", i, [NSThread currentThread]);
    }
    
    // 回到主线程进行操作
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"main---%@", [NSThread currentThread]);
    }];
}];

请添加图片描述

多线程的比较

请添加图片描述

pthread

pthread跨平台,使用难度大,需要手动管理线程生命周期

GCD与NSOperation

  • GCD的执行效率更高,执行的是由Block构成的任务,是一个轻量级的数据结构,写起来更加方便
  • GCD只支持FIFO队列,NSOperation可以通过设置最大并发数、设置优先级、添加依赖关系来调整执行顺序
  • NSOperation可以跨越队列设置依赖关系,GCD仅仅能通过栅栏等方法才能控制执行顺序
  • NSOperation更加面向对象,支持KVO,也可以通过继承等关系添加子类。

所以如果我们需要考虑异步操作之间的顺序行、依赖关系,比如多线程并发下载等等,就使用NSOperation。

GCD 与 NSThread 的区别

  • NSThread 通过 @selector 指定要执行的方法,代码分散, 依靠的是NSObject的分类实现的线程之间的通讯,如果要开线程必须创建多个线程对象。经常只用的是[NSTread currentThread] 查看当前的线程。
  • NSThread是一个控制线程执行的对象,它不如NSOperation抽象,通过它我们可以方便的得到一个线程,并控制它。但NSThread的线程之间的并发控制,是需要我们自己来控制的,可以通过NSCondition实现。
  • GCD 通过 block 指定要执行的代码,代码集中, 所有的代码写在一起的,让代码更加简单,易于阅读和维护,不需要管理线程的创建/销毁/复用的过程!程序员不用关心线程的生命周期

多线程的优缺点

优点

  • 使应用程序的响应速度更快,用户界面在进行其他工作的同时仍始终保持活动状态;
  • 优化任务执行,适当提高资源利用率(CPU,内存);
    缺点
  • 线程占用内存空间,管理线程需要额外CPU开销,开启大量线程,降低程序性能;
  • 增加程序复杂度,如线程间通信,多线程的资源共享等等

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/493219.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阶乘求和,求 1 + 2 + 3 + ... + 202320232023 ,阶乘总和的末尾九位数字

求 1! 2! 3! … 202320232023! &#xff0c;总和的末尾九位数字。 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;不仅仅是基础那么简单…… 地址&#xff1a;ht…

learn_C_deep_8 (循环语法的理解、void的用法以及理解)

目录 循环语法的理解 break关键字 continue关键字 continue跳转的位置 goto关键字 void的用法以及理解 void是否可以定义变量 为何 void 不能定义变量 void的应用场景 void 指针 循环语法的理解 for循环是一种常用的循环结构&#xff0c;它适合于在已知循环次数的情况…

ChatGPT prompt engineering (中文版)笔记 |吴恩达ChatGPT 提示工程

目录 一、资料二、 指南环境配置两个基本原则&#xff08;最重要!!!!&#xff09;原则一&#xff1a;编写清晰、具体的指令**策略一&#xff1a;使用分隔符清晰地表示输入的不同部分**&#xff0c;**分隔符可以是&#xff1a;&#xff0c;""&#xff0c;<>&…

浅谈几个通信概念-如何理解卷积,负频率,傅里叶变换,奈奎斯特采样定理?

1.如何理解卷积&#xff1f; t时刻的输出信号是t时刻之前的无数小的脉冲序列冲击引起的。 2. 如何理解欧拉公式&#xff0c;复指数信号呢&#xff1f; 可以看成一个点在复平面上以角速度w进行逆时针的旋转。 傅里叶分析&#xff1a; 整体到部分&#xff0c;把一个信号分解成无…

【网络】socket套接字基础知识

文章目录 IP与端口号TCP/UDP协议网络字节流socket套接字接口总结 IP与端口号 IP 每台主机都有自己的IP地址&#xff0c;所以当数据从一台主机传输到另一台主机就需要IP地址。报头中就会包含源IP和目的IP 源IP地址&#xff1a;发送数据报那个主机的IP地址&#xff0c;目的IP地…

JMeter开发web及手机APP自动化脚本练习

一、打开浏览器代理服务器设置 我这里用的是360浏览器&#xff0c;打开浏览器代理服务器设置&#xff0c;端口要与jmeter中的端口设置保持一致哦。 二、JMeter设置代理 JMeter设置代理&#xff08;jmeter中的端口要与360浏览器端口设置保持一致哦。&#xff09; 三、启动代理运…

BM17 二分查找-I

二分查找-I_牛客题霸_牛客网 (nowcoder.com) 设置中间值mid 每次判断目标值和中间值的大小 缩短区间 直到区间全被搜索完成 class Solution { public: /** * 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可 * * * param nums …

因子挖掘框架cs优缺点介绍和使用说明

cs框架的优点和缺点 优点和ts一样,就是速度非常快缺点有好几个:必须使用根据过去一定天数计算因子值,持有一定天数之后再平衡的模式;必须使用连续的数据,如果是期货期权等需要合成连续数据。资金不足的时候不会拒单。cs框架使用方法 设计理念 计算因子由用户进行计算,因…

Yolov7论文详解

论文地址&#xff1a;https://arxiv.org/pdf/2207.02696.pdfhttps://arxiv.org/pdf/2207.02696.pdf 项目地址&#xff1a; WongKinYiu/yolov7: Implementation of paper - YOLOv7: Trainable bag-of-freebies sets new state-of-the-art for real-time object detectors (gith…

多进程多线程并发服务器代码实现

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…

R语言:鉴于计算10亿以内训练模型记录for循环的加速

文章目录 1 前言2 几个循环2.1 100以内的和2.2 100以内奇数和/偶数和 3 多重循环3.1 向量化3.2 合并循环3.3 apply函数3.4 矩阵运算3.5 foreach分解任务 4 讨论 1 前言 笔者主力机是MBAM1芯片&#xff08;8256&#xff09;&#xff0c;某个下午巩固循环突然思考到个问题&#…

【Luenberger Observer】龙贝格观测器及示例Matlab仿真

目录 龙贝格观测器 龙贝格观测器示例和仿真 Matlab仿真 龙贝格观测器 观测器&#xff1a;根据系统的输入u和输出y估计系统的状态x。 SISO系统的状态空间方程如下 龙贝格观测器&#xff0c;通过在原系统添加基于输出误差校正项&#xff0c;构造状态空间方程&#xff0c;设x_h…

如何用Jmeter压测Netty的Echo服务之自定义Jmeter的Java Sampler

前言 如果想要压测一些三方组件&#xff0c;比如MQ&#xff0c;redis什么的&#xff0c;jmeter本身是不支持的。 本文以开发一个压测netty的echo示例&#xff0c;说明如何自定义jmeter的sampler。 开发 本文以idea示例&#xff0c; 新建工程 打开idea新建一个空的maven工程…

yolov8 ONNX Runtime C++ 部署

其实个人偏爱用OpenCV DNN 部署&#xff0c;但是在前面一篇博客发现还要升级OpenCV。 笔记本的CPU 是AMD 牌子的&#xff0c;就只能用 ONNX Runtime 部署了。 目录 Pre: cv::dnn::blobFromImages() gettimeofday() rand() template 代码 utils.h utils.cpp detect.h…

uboot第一阶段 start.S代码分析

u-boot.lds中找到start.S入口 (1)C语言规定整个项目的入口就是main函数。 (2)在uboot中因为有汇编阶段参与&#xff0c;因此不能直接找main.c。整个程序的入口取决于链接脚本中ENTRY声明的地方。ENTRY(_start)因此定义_start符号 的文件就是整个程序的起始文件&#xff0c;即st…

python报错提示以及logger的一些应用

本篇是拆解这篇【python︱函数、for、if、name、迭代器、防范报错、类定义、装饰器、argparse模块、yield 】 将报错 logger提示拿出来 文章目录 1、防范报错1.1 assert 断言 2 try...except...报错并提示异常信息优雅的异常报错&#xff1a;suppress 3、报错日志记录&#xf…

【Java 并发编程】一文详解 Java 内置锁 synchronized

一文详解 Java 内置锁 synchronized 1. 前言1.1 并发编程中存在线程安全问题1.2 设计同步器的意义1.3 如何解决线程并发安全问题&#xff1f; 2. synchronized 的用法2.1 修饰实例方法&#xff0c;锁是当前实例对象2.2 修饰静态方法&#xff0c;锁是当前类对象2.3 修饰代码块&a…

简单创建SSM项目

1、在idea中创建项目 点击new-project&#xff0c;点击Maven项目&#xff0c;勾选 creat from archetype &#xff0c;找到maven-archetype-webapp 写好相关信息 点击下一步&#xff0c;需要检查maven环境 点击后下载对应的插件&#xff0c;选择项目地址。 开始下载资源&#x…

rust vscode编辑器常用插件与配置

插件&#xff1a;rust-analyzer 会实时编译和分析你的 Rust 代码&#xff0c;提示代码中的错误&#xff0c;并对类型进行标注 插件的完整手册地址&#xff1a;https://rust-analyzer.github.io/manual.html。 插件&#xff1a; rust syntax 为代码提供语法高亮。 …

就挺无语的,这是有脾气的博客

文章目录 前言1. 背景2. 使用3. 公众号体验4. 结束语 前言 ChatGPT已经推出两个多月了&#xff0c;热度已经不减。ChatGPT由人工智能研究实验室OpenAI在2022年11月30日发布的全新聊天机器人模型&#xff0c;一款人工智能技术驱动的自然语言处理工具。它能够通过学习和理解人类的…