【iOS】NSOperation、NSOperationQueue

文章目录

  • 前言
  • 一、NSOperation、NSOperationQueue 简介
  • 二、NSOperation、NSOperationQueue 操作和操作队列
  • 三、NSOperation
  • 四、NSOperationQueue
  • 五、NSOperationQueue 控制串行执行、并发执行
  • 六、 NSOperation 操作依赖
  • 七、NSOperation 优先级
  • 八、NSOperation、NSOperationQueue 线程间的通信
  • 九、NSOperation、NSOperationQueue 线程同步和线程安全
    • NSOperation、NSOperationQueue 非线程安全
    • NSOperation、NSOperationQueue 非线程安全
  • 十、NSOperation、NSOperationQueue 常用属性和方法归纳
    • NSOperation 常用属性和方法
    • NSOperationQueue 常用属性和方法


前言

寒假学习了GCD,前几天学习了NSThread,今天来学习iOS 多线程中 NSOperation、NSOperationQueue 的相关知识以及使用方法。

一、NSOperation、NSOperationQueue 简介

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

NSOperation需要配合NSOperationQueue来实现多线程。

二、NSOperation、NSOperationQueue 操作和操作队列

由于NSOperation、NSOperationQueue 是GCD的更高级的封装,因此两者有一些相同的概念

  • 操作(Operation)
    执行操作,也就相当于我们在线程中执行的代码
    在GCD操作是放在Block中的,在 NSOperation 中,我们使用 NSOperation 子类 NSInvocationOperation、NSBlockOperation,或者自定义子类来封装操作
  • 操作队列(Operation Queues)
  • 这里的队列是指操作队列,即用来存放操作的队列。不同于GCD的先进先出,NSOperationQueue可以设置操作执行的优先级
  • 操作队列通过设置最大并发操作数(maxConcurrentOperationCount) 来控制并发、串行。
  • NSOperationQueue 为我们提供了两种不同类型的队列:主队列和自定义队列。主队列运行在主线程之上,而自定义队列在后台执行。

三、NSOperation

NSOperatino实现多线程的步骤如下:

  • 1、创建任务:先将需要执行的操作封装到NSOperation对象中。
  • 2、创建队列:创建NSOperationQueue
  • 3、将任务加入到队列中:将NSOperation对象添加到NSOperationQueue

在不使用 NSOperationQueue,单独使用 NSOperation 的情况下系统同步执行操作

- (void)applicationDidFinishLaunching:(UIApplication *)application {MyOperation *operation = [[MyOperation alloc] init];NSLog(@"Before operation start");[operation start];NSLog(@"After operation finish");
}

操作从开始到结束的输出都是按顺序且同步的。应用的执行将在 Before operation start 打印后直接开始 NSOperation,然后直到该操作完成后,才继续执行打印 After operation finish。由于 start 方法是同步执行的,所以它会阻塞当前线程直到操作完成。

//基本使用
- (void)testBaseNSOperation{//处理事务NSInvocationOperation *op =  [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"XN"]; // object是传递参数NSOperationQueue *queue = [[NSOperationQueue alloc] init];//操作加入队列[queue addOperation:op];   
}
- (void)handleInvocation:(id)operation{NSLog(@"%@ - %@", operation, [NSThread currentThread]);
}

NSOperation是个抽象类,实际运用时中需要使用它的子类

  • 1、使用子类NSInvocationOperation
//直接处理事务,不添加隐性队列
- (void)createNSOperation{//创建NSInvocationOperation对象并关联方法,之后start。NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"xn"];[invocationOperation start];
}

在这里插入图片描述

当我们直接使用[invocationOperation start];时任务在主线程执行

  • 2、使用子类NSBlockOperation
- (void)useBlockOperation {// 1.创建 NSBlockOperation 对象NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{for (int i = 0; i < 2; i++) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程}}];// 2.调用 start 方法开始执行操作[op start];
}

在这里插入图片描述
在没有使用NSOperationQueue,同时没有使用addExecutionBlock方法时不会开启新线程执行操作

同时通过addExecutionBlock就可以开启新线程执行操作

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

- (void)testNSBlockOperationExecution{// 1.创建 NSBlockOperation 对象NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{for (int i = 0; i < 2; i++) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程}}];// 2.添加额外的操作[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 addExecutionBlock:^{for (int i = 0; i < 2; i++) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@"5---%@", [NSThread currentThread]); // 打印当前线程}}];[op addExecutionBlock:^{for (int i = 0; i < 2; i++) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@"6---%@", [NSThread currentThread]); // 打印当前线程}}];[op addExecutionBlock:^{for (int i = 0; i < 2; i++) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@"7---%@", [NSThread currentThread]); // 打印当前线程}}];[op addExecutionBlock:^{for (int i = 0; i < 2; i++) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@"8---%@", [NSThread currentThread]); // 打印当前线程}}];// 3.调用 start 方法开始执行操作[op start];
}

输出:
在这里插入图片描述
使用子类 NSBlockOperation,并调用方法 AddExecutionBlock: 的情况下,blockOperationWithBlock:方法中的操作 和 addExecutionBlock: 中的操作是在不同的线程中异步执行的

  • 3、定义继承自NSOperation的子类,通过实现内部相应的方法来封装任务。
//*********自定义继承自NSOperation的子类*********
@interface CJLOperation : NSOperation
@end@implementation CJLOperation
- (void)main{for (int i = 0; i < 3; i++) {NSLog(@"NSOperation的子类:%@",[NSThread currentThread]);}
}
@end//*********使用*********
- (void)cjl_testCJLOperation{//运用继承自NSOperation的子类 首先我们定义一个继承自NSOperation的类,然后重写它的main方法。CJLOperation *operation = [[CJLOperation alloc] init];[operation start];
}

输出:
在这里插入图片描述
可以看出:在没有使用 NSOperationQueue、在主线程单独使用自定义继承自 NSOperation 的子类的情况下,是在主线程执行操作,并没有开启新线程

四、NSOperationQueue

NSOperationQueue添加事务
NSOperationQueue有两种队列:主队列、其他队列。其他队列包含了 串行和并发

  • 主队列
    凡是添加到主队列中的操作,都会放到主线程中执行。
// 主队列获取方法
NSOperationQueue *queue = [NSOperationQueue mainQueue];
  • 自定义队列(非主队列)
    添加到这种队列中的操作,就会自动放到子线程中执行
    同时包含了:串行、并发功能。
// 自定义队列创建方法
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

将操作加入到队列中

上边我们说到 NSOperation 需要配合 NSOperationQueue 来实现多线程。

那么我们需要将创建好的操作加入到队列中去。总共有两种方法:
1、- (void)addOperation:(NSOperation *)op;
需要先创建操作,再将创建好的操作加入到创建好的队列中去。

/*** 使用 addOperation: 将操作加入到操作队列中*/
- (void)addOperationToQueue {// 1.创建队列NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 2.创建操作// 使用 NSInvocationOperation 创建操作1NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];// 使用 NSInvocationOperation 创建操作2NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];// 使用 NSBlockOperation 创建操作3NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{for (int i = 0; i < 2; i++) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@"3---%@", [NSThread currentThread]); // 打印当前线程}}];[op3 addExecutionBlock:^{for (int i = 0; i < 2; i++) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@"4---%@", [NSThread currentThread]); // 打印当前线程}}];// 3.使用 addOperation: 添加所有操作到队列中[queue addOperation:op1]; // [op1 start][queue addOperation:op2]; // [op2 start][queue addOperation:op3]; // [op3 start]
}

使用 NSOperation 子类创建操作,并使用 addOperation: 将操作加入到操作队列后能够开启新线程,进行并发执行

NSOperationQueue 管理一个或多个 NSOperation 对象的执行。当你将 NSOperation 实例加入到 NSOperationQueue 时,你不需要手动创建和管理线程。操作队列自动处理这些细节。

2、- (void)addOperationWithBlock:(void (^)(void))block;
无需先创建操作,在 block 中添加操作,程序直接将包含操作的 block 加入到队列中

/*** 使用 addOperationWithBlock: 将操作加入到操作队列中*/- (void)addOperationWithBlockToQueue {// 1.创建队列NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 2.使用 addOperationWithBlock: 添加操作到队列中[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]); // 打印当前线程}}];
}

在这里插入图片描述
可以看出:使用 addOperationWithBlock: 将操作加入到操作队列后能够开启新线程,进行并发执行

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

我们在上面演示了并发功能,我们来看一下它的串行功能如何实现

这里有个关键属性 maxConcurrentOperationCount ,叫做最大并发操作数。用来控制一个特定队列中可以有多少个操作同时参与并发执行。

注意:这里 maxConcurrentOperationCount 控制的不是并发线程的数量,而是一个队列中同时能并发执行的最大操作数。而且一个操作也并非只能在一个线程中运行,一个操作可能很长,可以将其分散在多个线程中执行,比如我们在子线程更新数据后在主线程更新UI。

最大并发操作数:maxConcurrentOperationCount

maxConcurrentOperationCount 默认情况下为-1,表示不进行限制,可进行并发执行
maxConcurrentOperationCount 为1时,队列为串行队列。只能串行执行。
maxConcurrentOperationCount 大于1时,队列为并发队列。操作并发执行,当然这个值不应超过系统限制,即使自己设置一个很大的值,系统也会自动调整为 min{自己设定的值,系统设定的默认最大值}。

/*** 设置 MaxConcurrentOperationCount(最大并发操作数)*/
- (void)setMaxConcurrentOperationCount {// 1.创建队列NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 2.设置最大并发操作数queue.maxConcurrentOperationCount = 1; // 串行队列
// queue.maxConcurrentOperationCount = 2; // 并发队列
// queue.maxConcurrentOperationCount = 8; // 并发队列// 3.添加操作[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]); // 打印当前线程}}];[queue addOperationWithBlock:^{for (int i = 0; i < 2; i++) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@"4---%@", [NSThread currentThread]); // 打印当前线程}}];
}

maxConcurrentOperationCount = 1
在这里插入图片描述

可以看到任务按顺序串行执行

maxConcurrentOperationCount = 2

在这里插入图片描述
并发操作数为2时可以执行两个操作,操作时并发施行的,而开启线程数量是由系统决定的,不需要我们来管理。

因为NSOperation由系统控制开启线程数量以及串行并行,而GCD需要我们手动控制

六、 NSOperation 操作依赖

NSOperation、NSOperationQueue 最吸引人的地方是它能添加操作之间的依赖关系。通过操作依赖,我们可以很方便的控制操作之间的执行先后顺序。NSOperation 提供了3个接口供我们管理和查看依赖。

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

我们现在来假设一个场景,有两个操作A、B,A操作完B才能操作,也就是B依赖A

/*** 操作依赖* 使用方法:addDependency:*/
- (void)addDependency {// 1.创建队列NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 2.创建操作NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{for (int i = 0; i < 2; i++) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程}}];NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{for (int i = 0; i < 2; i++) {[NSThread sleepForTimeInterval:2]; // 模拟耗时操作NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程}}];// 3.添加依赖[op2 addDependency:op1]; // 让op2 依赖于 op1,则先执行op1,在执行op2// 4.添加操作到队列中[queue addOperation:op2];[queue addOperation:op1];
}

可以看到即使两个操作在不同线程执行也就是并发执行,也是op1先执行才能执行op2
在这里插入图片描述
去掉依赖:
在这里插入图片描述

七、NSOperation 优先级

NSOperation 提供了queuePriority(优先级)属性,queuePriority属性适用于同一操作队列中的操作

默认情况下,所有新创建的操作对象优先级都是NSOperationQueuePriorityNormal,但是我们可以通过setQueuePriority:方法来改变当前操作在同一队列中的执行优先级

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

将操作添加到队列后操作会进入就绪状态,就绪状态的操作的执行顺序由操作之间的优先级决定

就绪状态的操作:简单理解就是准备就绪的操作就是没有需要依赖的操作,例如op2依赖于op1,那么op2就没有准备就绪,因为他需要等op1执行完才准备就绪

优先级不能取代依赖关系。如果要控制操作间的启动顺序,则必须使用依赖关系。

八、NSOperation、NSOperationQueue 线程间的通信

同样的我们在开发中常常在子线程中更新数据在主线程中更新UI,这在NSOperation中时如何实现的呢

/*** 线程间通信*/
- (void)communication {// 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]); // 打印当前线程}}];}];
}

输出:
在这里插入图片描述

九、NSOperation、NSOperationQueue 线程同步和线程安全

又到了老生常谈的售票问题

NSOperation、NSOperationQueue 非线程安全

因为使用NSOperation将操作添加到队列时系统后自动执行并发操作
因此如果不对线程进行加锁会出现如下状况

/*** 非线程安全:不使用 NSLock* 初始化火车票数量、卖票窗口(非线程安全)、并开始卖票*/
- (void)initTicketStatusNotSave {NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程self.ticketSurplusCount = 50;// 1.创建 queue1,queue1 代表北京火车票售卖窗口NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];queue1.maxConcurrentOperationCount = 1;// 2.创建 queue2,queue2 代表上海火车票售卖窗口NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];queue2.maxConcurrentOperationCount = 1;// 3.创建卖票操作 op1__weak typeof(self) weakSelf = self;NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{[weakSelf saleTicketNotSafe];}];// 4.创建卖票操作 op2NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{[weakSelf saleTicketNotSafe];}];// 5.添加操作,开始卖票[queue1 addOperation:op1];[queue2 addOperation:op2];
}/*** 售卖火车票(非线程安全)*/
- (void)saleTicketNotSafe {while (1) {if (self.ticketSurplusCount > 0) {//如果还有票,继续售卖self.ticketSurplusCount--;NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);[NSThread sleepForTimeInterval:0.2];} else {NSLog(@"所有火车票均已售完");break;}}
}

输出:
在这里插入图片描述

NSOperation、NSOperationQueue 非线程安全

使用互斥锁进行加锁后就得到了正确的票数

- (void)saleTicketNotSafe {while (1) {@synchronized (self) {if (self.ticketSurplusCount > 0) {//如果还有票,继续售卖self.ticketSurplusCount--;NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);[NSThread sleepForTimeInterval:0.2];} else {NSLog(@"所有火车票均已售完");break;}}}
}

输出:
在这里插入图片描述

十、NSOperation、NSOperationQueue 常用属性和方法归纳

NSOperation 常用属性和方法

取消操作方法

  • - (void)cancel; 可取消操作,实质是标记 isCancelled 状态。

判断操作状态方法

  • - (BOOL)isFinished; 判断操作是否已经结束。
  • - (BOOL)isCancelled; 判断操作是否已经标记为取消。
  • - (BOOL)isExecuting; 判断操作是否正在在运行。
  • - (BOOL)isReady; 判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关。

操作同步

  • -(void)waitUntilFinished; 阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步。
  • - (void)setCompletionBlock:(void (^)(void))block; completionBlock 会在当前操作执行完毕时执行 completionBlock。
  • - (void)addDependency:(NSOperation *)op; 添加依赖,使当前操作依赖于操作 op 的完成。
  • - (void)removeDependency:(NSOperation *)op;移除依赖,取消当前操作对操作 op 的依赖。
  • @property (readonly, copy) NSArray<NSOperation *> *dependencies; 在当前操作开始执行之前完成执行的所有操作对象数组。

NSOperationQueue 常用属性和方法

取消/暂停/恢复操作

- (void)cancelAllOperations; 可以取消队列的所有操作。
- (BOOL)isSuspended; 判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态。
- (void)setSuspended:(BOOL)b; 可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列。

操作同步

- (void)waitUntilAllOperationsAreFinished; 阻塞当前线程,直到队列中的操作全部执行完毕。

添加/获取操作

- (void)addOperationWithBlock:(void (^)(void))block; 向队列中添加一个 NSBlockOperation 类型操作对象。
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait; 向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束
- (NSArray *)operations; 当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)。
- (NSUInteger)operationCount; 当前队列中的操作数。

获取队列

+ (id)currentQueue; 获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil。
+ (id)mainQueue; 获取主队列。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【python】基础语法

目录 一.注释和常见规则 二.变量及类型 1.数据类型 2.Numbers数字数据类型 3. 字符串类型 &#xff08;1&#xff09;字符串访问 &#xff08;2&#xff09;字符串拼接 4.List&#xff08;列表&#xff09;类型 &#xff08;1&#xff09; 定义 &#xff08;2&#…

STM32开启停止模式,用外部中断唤醒程序运行

今天学习了一下STM32的停止模式&#xff0c;停止模式下&#xff0c;所有外设的时钟和CPU的电源都会被关闭&#xff0c;所以会很省电&#xff0c;打破这种停止模式的方式就是外部中断可以唤醒停止模式。要想实现这个功能&#xff0c;其实设置很简单的&#xff0c;总共就需要两步…

websevere服务器从零搭建到上线(四)|muduo网络库的基本原理和使用

文章目录 muduo源码编译安装muduo框架讲解muduo库编写服务器代码示例代码解析用户连接的创建和断开回调函数用户读写事件回调 使用vscode编译程序配置c_cpp_properties.json配置tasks.json配置launch.json编译 总结 muduo源码编译安装 muduo依赖Boost库&#xff0c;所以我们应…

五月节放假作业讲解

目录 作业1&#xff1a; 问题&#xff1a; 结果如下 作业2&#xff1a; 结果: 作业1&#xff1a; 初始化数组 问题&#xff1a; 如果让数组初始化非0数会有问题 有同学就问了&#xff0c;我明明已经初始化定义过了&#xff0c;为啥还有0呀 其实这种初始化只会改变第一个…

Fluent 区域交界面的热边界条件

多个实体域公共交界面的壁面&#xff0c;Fluent 会分拆为 wall 和 wall-shadow 的两个壁面&#xff0c;两者为配对关系&#xff0c;分别从属于一个实体域。 配对面可使用热通量、温度、耦合三类热边界条件&#xff0c;前两者统称为非耦合热边界条件。 耦合为配对面默认的热边界…

MYSQL基础架构、执行过程分析、事务的实现、索引的选择、覆盖索引

本文是mysql45讲的1-5的总结 文章目录 基础架构连接器分析器优化器执行器SQL查询执行过程详细执行步骤 SQL更新执行过程重要的日志模块&#xff1a;redo log重要的日志模块&#xff1a;binlog阶段性提交 事务事务隔离的实现启动 索引数据库索引模型InnoDB索引组织结构主键选择…

【深度学习】第二门课 改善深层神经网络 Week 2 3 优化算法、超参数调试和BN及其框架

&#x1f680;Write In Front&#x1f680; &#x1f4dd;个人主页&#xff1a;令夏二十三 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;深度学习 &#x1f4ac;总结&#xff1a;希望你看完之后&#xff0c;能对…

Qt5 框架学习及应用 — 对象树

Qt 对象树 对象树概念Qt为什么使用对象树 &#xff1f;将对象挂到对象树上 对象树概念 对象树&#xff1a;对于树的概念&#xff0c;相信许多学过数据结构的同学应该都不会陌生。在学习数据结构的时候我们所接触的什么二叉树、多叉树、哈夫曼树、AVL树、再到红黑树、B/B树………

【LeetCode刷题】739. 每日温度(单调栈)

1. 题目链接2. 题目描述3. 解题方法4. 代码 1. 题目链接 739. 每日温度 2. 题目描述 3. 解题方法 用一个栈st保存每个数的下标&#xff0c;同时创建一个数组res保存结果&#xff0c;初始值都为0。循环遍历题目中的数组temperature。如果temperature[i] > st.top()&#x…

Vue入门到关门之Vue3项目创建

一、vue3介绍 1、为什么要学习vue3&#xff1f; vue3的变化&#xff1a; 首先vue3完全兼容vue2&#xff0c;但是vue3不建议用vue2的写法&#xff1b;其次&#xff0c;vue3拥抱TypeScript&#xff0c;之前vue2使用的JavaScript&#xff0c;ts完全兼容js 最后之前学的vue2 是…

pytest教程-36-钩子函数-pytest_collection_start

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了pytest_unconfigure钩子函数的使用方法&#xff0c;本小节我们讲解一下pytest_collection_start钩子函数的使用方法。 pytest_collection_start(session) 是一个 pytest 钩子函数&#xff0c;…

深度学习之DCGAN

目录 须知 转置卷积 DCGAN 什么是DCGAN 生成器代码 判别器代码 补充知识 LeakyReLU&#xff08;x&#xff09; torch.nn.Dropout torch.nn.Dropout2d DCGAN完整代码 运行结果 图形显示 须知 在讲解DCGAN之前我们首先要了解转置卷积和GAN 关于GAN在这片博客中已经很…

LLVM的ThinLTO编译优化技术在Postgresql中的应用

部分内容引用&#xff1a;https://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html LTO是什么&#xff1f; 链接时优化&#xff08;Link-time optimization&#xff0c;简称LTO&#xff09;是编译器在链接时对程序进行的一种优化。它适用于以文件为单位编译…

怎么ai自动答题?方法揭晓!

怎么ai自动答题&#xff1f;在数字化和信息化的浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;技术日新月异&#xff0c;逐渐渗透到我们生活的方方面面。其中&#xff0c;AI自动答题软件作为辅助学习的工具&#xff0c;受到了越来越多学生和考生的青睐。它们不仅能够帮…

欢乐钓鱼大师脚本,游戏托管一键操作!

欢迎来到《钓鱼大师乐趣无穷》&#xff01;这里是一片充满了乐趣和挑战的钓鱼天地。不论你是刚刚入门的小白&#xff0c;还是已经成为老手的大神&#xff0c;本攻略将为你揭示如何在游戏中获得成功&#xff0c;并针对稀有鱼类的钓鱼技巧进行详细介绍。 一、初探钓鱼的乐趣 在《…

Bookends for Mac:文献管理工具

Bookends for Mac&#xff0c;一款专为学术、研究和写作领域设计的文献管理工具&#xff0c;以其强大而高效的功能深受用户喜爱。这款软件支持多种文件格式&#xff0c;如PDF、DOC、RTF等&#xff0c;能够自动提取文献的关键信息&#xff0c;如作者、标题、出版社等&#xff0c…

0506_IO1

思维导图&#xff1a; 练习&#xff1a; 有如下结构体 struct Student{ char name[16]; int age; double math_score; double chinese_score; double english_score; double physics_score; double chemistry_score; double bio_score; }; 申请该结构体数组&#xff0c;容量为…

【简单介绍下7-Zip】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

【春招特供】Unity面试题总结 | Unity基础篇

物体发生碰撞的必要条件&#xff1f; 两个物体都必须带有碰撞器&#xff08;Collider&#xff09;&#xff0c;其中一个物体还必须带有Rigidbody刚体&#xff0c;而且必须是运动的物体带有Rigidbody脚本才能检测到碰撞。 2. Unity3d中的碰撞器和触发器的区别&#xff1f; 碰…

Spring Security + JWT 实现登录认证和权限控制

Spring Security JWT 实现登录认证和权限控制 准备步骤 准备好一些常用的工具类&#xff0c;比如jwtUtil&#xff0c;redisUtil等。引入数据库&#xff0c;mybatis等&#xff0c;配置好controller&#xff0c;service&#xff0c;mapper&#xff0c;保证能够正常的数据请求。…