多线程之NSOperation

套话

GCD一样,NSOperation也是我们日常开发中经常用到的多线程技术。本文将会介绍NSOperation的基本使用、添加依赖、

初次使用

NSOperation是个抽象类,依赖于子类NSInvocationOperationNSBlockOperation去实现

下面是开发者文档上对NSOperation的一段描述请添加图片描述

NSInvocationOperation

基本使用

- (void)test {// 处理事务NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:selfselector:@selector(handleInvocation:) object:@"Felix"];// 创建队列NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 操作加入队列[queue addOperation:op];
}- (void)handleInvocation:(id)operation {NSLog(@"%@ --- %@",operation, [NSThread currentThread]);
}
--------------------输出结果:-------------------
Felix --- <NSThread: 0x6000000422c0>{number = 3, name = (null)}
--------------------输出结果:-------------------

直接处理事务,不添加隐性队列

- (void)test {NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"Felix"];[op start];
}
- (void)test {NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"Felix"];NSOperationQueue *queue = [[NSOperationQueue alloc] init];[queue addOperation:op];[op start];
}

请添加图片描述

上述代码之所以会崩溃,是因为线程生命周期:

  • queue addOperation:op已经将处理事务的操作任务加入到队列中,并让线程运行
  • op start将已经运行的线程再次运行会造成线程混乱

NSBlockOperation

NSInvocationOperationNSBlockOperation两者的区别在于:

  • 前者类似target形式
  • 后者类似block形式——函数式编程,业务逻辑代码可读性更高
- (void)test {// 初始化添加事务NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"任务1————%@",[NSThread currentThread]);}];// 添加事务[bo addExecutionBlock:^{NSLog(@"任务2————%@",[NSThread currentThread]);}];// 回调监听bo.completionBlock = ^{NSLog(@"完成了!!!");};NSOperationQueue *queue = [[NSOperationQueue alloc] init];[queue addOperation:bo];NSLog(@"事务添加进了NSOperationQueue");
}--------------------输出结果:-------------------
事务添加进了NSOperationQueue
任务1————<NSThread: 0x6000032dc1c0>{number = 5, name = (null)}
任务2————<NSThread: 0x6000032a1880>{number = 4, name = (null)}
完成了!!!
--------------------输出结果:-------------------

NSOperationQueue是异步执行的,所以任务一任务二的完成顺序不确定

执行顺序

下列代码可以证明操作与队列的执行效果是异步并发

- (void)test {NSOperationQueue *queue = [[NSOperationQueue alloc] init];for (int i = 0; i < 5; i++) {[queue addOperationWithBlock:^{NSLog(@"%@---%d", [NSThread currentThread], i);}];}
}
--------------------输出结果:-------------------
<NSThread: 0x600002771600>{number = 3, name = (null)}---0
<NSThread: 0x60000277ac80>{number = 7, name = (null)}---3
<NSThread: 0x600002774840>{number = 6, name = (null)}---2
<NSThread: 0x600002776a80>{number = 8, name = (null)}---4
<NSThread: 0x60000270c540>{number = 5, name = (null)}---1
--------------------输出结果:-------------------

设置优先级

NSOperation设置优先级只会让CPU有更高的几率调用,不是说设置高就一定全部先完成

  • 不使用sleep——高优先级的任务一先于低优先级的任务二
- (void)test {NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{for (int i = 0; i < 5; i++) {//sleep(1);NSLog(@"第一个操作 %d --- %@", i, [NSThread currentThread]);}}];// 设置最高优先级bo1.qualityOfService = NSQualityOfServiceUserInteractive;NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{for (int i = 0; i < 5; i++) {NSLog(@"第二个操作 %d --- %@", i, [NSThread currentThread]);}}];// 设置最低优先级bo2.qualityOfService = NSQualityOfServiceBackground;NSOperationQueue *queue = [[NSOperationQueue alloc] init];[queue addOperation:bo1];[queue addOperation:bo2];
}--------------------输出结果:-------------------
第一个操作 0 --- <NSThread: 0x600002254280>{number = 6, name = (null)}
第一个操作 1 --- <NSThread: 0x600002254280>{number = 6, name = (null)}
第一个操作 2 --- <NSThread: 0x600002254280>{number = 6, name = (null)}
第一个操作 3 --- <NSThread: 0x600002254280>{number = 6, name = (null)}
第一个操作 4 --- <NSThread: 0x600002254280>{number = 6, name = (null)}
第二个操作 0 --- <NSThread: 0x600002240340>{number = 7, name = (null)}
第二个操作 1 --- <NSThread: 0x600002240340>{number = 7, name = (null)}
第二个操作 2 --- <NSThread: 0x600002240340>{number = 7, name = (null)}
第二个操作 3 --- <NSThread: 0x600002240340>{number = 7, name = (null)}
第二个操作 4 --- <NSThread: 0x600002240340>{number = 7, name = (null)}
--------------------输出结果:-------------------
  • 使用sleep进行延时——高优先级的任务一慢于低优先级的任务二
第二个操作 0 --- <NSThread: 0x600002b35840>{number = 7, name = (null)}
第二个操作 1 --- <NSThread: 0x600002b35840>{number = 7, name = (null)}
第二个操作 2 --- <NSThread: 0x600002b35840>{number = 7, name = (null)}
第二个操作 3 --- <NSThread: 0x600002b35840>{number = 7, name = (null)}
第二个操作 4 --- <NSThread: 0x600002b35840>{number = 7, name = (null)}
第一个操作 0 --- <NSThread: 0x600002b3c700>{number = 5, name = (null)}
第一个操作 1 --- <NSThread: 0x600002b3c700>{number = 5, name = (null)}
第一个操作 2 --- <NSThread: 0x600002b3c700>{number = 5, name = (null)}
第一个操作 3 --- <NSThread: 0x600002b3c700>{number = 5, name = (null)}
第一个操作 4 --- <NSThread: 0x600002b3c700>{number = 5, name = (null)}

线程间通讯

  • GCD中使用异步进行网络请求,然后回到主线程刷新UI
  • NSOperation中也有类似在线程间通讯的操作
- (void)test {NSOperationQueue *queue = [[NSOperationQueue alloc] init];queue.name = @"Felix";[queue addOperationWithBlock:^{NSLog(@"请求网络%@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]);[[NSOperationQueue mainQueue] addOperationWithBlock:^{NSLog(@"刷新UI%@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]);}];}];
}
--------------------输出结果:-------------------
请求网络<NSOperationQueue: 0x7ff4a240bae0>{name = 'Felix'}--<NSThread: 0x6000007dcf00>{number = 5, name = (null)}
刷新UI<NSOperationQueue: 0x7ff4a24087d0>{name = 'NSOperationQueue Main Queue'}--<NSThread: 0x60000078c8c0>{number = 1, name = main}
--------------------输出结果:-------------------

设置并发数

  • GCD中只能使用信号量来设置并发数
  • NSOperation轻易就能设置并发数
    • 通过设置maxConcurrentOperationCount来控制单次出队列去执行的任务数
- (void)test {NSOperationQueue *queue = [[NSOperationQueue alloc] init];queue.name = @"Felix";queue.maxConcurrentOperationCount = 2;for (int i = 0; i < 5; i++) {[queue addOperationWithBlock:^{ // 一个任务[NSThread sleepForTimeInterval:2];NSLog(@"%d-%@",i,[NSThread currentThread]);}];}
}
--------------------输出结果:-------------------
1-<NSThread: 0x6000009290c0>{number = 5, name = (null)}
0-<NSThread: 0x6000009348c0>{number = 8, name = (null)}
3-<NSThread: 0x6000009290c0>{number = 5, name = (null)}
2-<NSThread: 0x60000094b0c0>{number = 7, name = (null)}
4-<NSThread: 0x6000009348c0>{number = 8, name = (null)}
--------------------输出结果:-------------------

添加依赖

NSOperation中添加依赖能很好的控制任务执行的先后顺序

- (void)test {NSOperationQueue *queue = [[NSOperationQueue alloc] init];NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:0.5];NSLog(@"请求token");}];NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:0.5];NSLog(@"拿着token,请求数据1");}];NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:0.5];NSLog(@"拿着数据1,请求数据2");}];//添加依赖[bo2 addDependency:bo1];[bo3 addDependency:bo2];[queue addOperations:@[bo1,bo2,bo3] waitUntilFinished:YES];NSLog(@"执行完了?我要干其他事");
}--------------------输出结果:-------------------
请求token
拿着token,请求数据1
拿着数据1,请求数据2
执行完了?我要干其他事
--------------------输出结果:-------------------

注意不要添加依赖导致循环运用,会导致依赖无效并会在控制台打印出"XPC connection interrupted"请添加图片描述

任务的挂起、继续、取消

// 挂起
queue.suspended = YES;
// 继续
queue.suspended = NO;
// 取消
[queue cancelAllOperations];

请添加图片描述
这幅图是并发量为2的情况:

  • 挂起前:任务3、任务4等待被调度
  • 挂起瞬间:任务3、任务4已经被调度出队列,准备执行,此时它们是无法挂起的
  • 挂起后:任务3、任务4被线程执行,而原来的队列被挂起不能被调度

自定义NSOperation缓存机制

NSOperation异步处理

NSOperationQueue *queue = [[NSOperationQueue alloc] init];UIImageView *imageView = [[UIImageView alloc] init];imageView.frame = CGRectMake(100, 100, 100, 100);[self.view addSubview:imageView];NSString *urlStr = [NSString stringWithFormat:@"https://profile-avatar.csdnimg.cn/910a183044d54231b5e418c23a558516_weixin_61196797.jpg!1"];NSURL *imageURL = [NSURL URLWithString:urlStr];NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"去下载图片" );// 延迟NSData *data   = [NSData dataWithContentsOfURL:imageURL];UIImage *image = [UIImage imageWithData:data];// 更新UI[[NSOperationQueue mainQueue] addOperationWithBlock:^{imageView.image = image;NSLog(@"加载完成");imageView.backgroundColor = [UIColor redColor];}];}];[queue addOperation:bo];

请添加图片描述

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

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

相关文章

macOS 虚拟桌面黑屏(转)

转自&#xff1a;macOS重置虚拟桌面、macOS 虚拟桌面黑屏 有几次出现如图的情况&#xff0c;以为是iTerm的问题&#xff0c;但是在关闭软件&#xff0c;重启之后&#xff0c;依旧无效。 后面经过网友告知&#xff0c;才知道是虚拟桌面的问题。 为了清理这个问题&#xff0c;有以…

ELD透明屏在智能家居中有哪些优点展示?

ELD透明屏是一种新型的显示技术&#xff0c;它能够在不需要背光的情况下显示图像和文字。 ELD透明屏的原理是利用电致发光效应&#xff0c;通过在透明基板上涂覆一层特殊的发光材料&#xff0c;当电流通过时&#xff0c;发光材料会发出光线&#xff0c;从而实现显示效果。 ELD…

论文研读|多媒体自动评论生成发展综述

前言&#xff1a;多媒体自动评论生成旨在通过使用生成模型&#xff0c;对给定上下文生成符合情境的评论&#xff0c;近年来&#xff0c;随着图像描述等跨模态工作取得较大突破&#xff0c;相关研究也逐渐展开。评论作为社交平台互动的重要组成部分&#xff0c;在引导舆论、提升…

python-MySQL数据库建表语句(需要连接数据库)转存为Excel文档-工作小记

将create table XXXXXX 转为指定Excel文档。该脚本适用于数据库表结构本地文档记录 呈现效果 代码 # -*- coding:utf-8 -*- # Time : 2023/8/2 15:14 # Author: 水兵没月 # File : MySQL建表_2_excel.py import reimport mysql.connector import pandas as pd db 库名 mydb …

Linux的基本指令(2)

指令1&#xff1a;man 作用&#xff1a;可以查询linux指令语法内容。 格式&#xff1a; man 指令 安装man指令&#xff1a; yum install -y man-pages 例如&#xff1a; 查询 指令 ls 的语法内容。 man ls 查询 fork 指令的语法内容。 man fork 在man中存在9个手册&…

【腾讯云Cloud Studio实战训练营】使用Cloud Studio迅捷开发一个3D家具个性化定制应用

目录 前言&#xff1a; 一、腾讯云 Cloud Studio介绍&#xff1a; 1、接近本地 IDE 的开发体验 2、多环境可选&#xff0c;或连接到云主机 3、随时分享预览效果 4、兼容 VSCode 插件 5、 AI代码助手 二、腾讯云Cloud Studio项目实践&#xff08;3D家具个性化定制应用&…

Day51 算法记录| 动态规划 18(单调栈)

单调栈 739. 每日温度496.下一个更大元素 I503. 下一个更大元素 II42. 接雨水84. 柱状图中最大的矩形 单调栈&#xff1a;找最近的比他大的值 最近大的值&#xff1a;需要一个单调递减的栈&#xff08;大于栈顶元素就弹出&#xff09; 最近最小值&#xff1a;单调递减栈 方向&a…

idea-常用插件汇总

idea-常用插件汇总 码云插件 这个插件是码云提供的ps-码云是国内的一款类似github的代码托管工具。 Lombok Lombok是一个通用Java类库&#xff0c;能自动插入编辑器并构建工具&#xff0c;简化Java开发。通过添加注解的方式&#xff0c;不需要为类编写getter或setter等方法…

记一次 .NET 某物流API系统 CPU爆高分析

一&#xff1a;背景 1. 讲故事 前段时间有位朋友找到我&#xff0c;说他程序CPU直接被打满了&#xff0c;让我帮忙看下怎么回事&#xff0c;截图如下&#xff1a; 看了下是两个相同的程序&#xff0c;既然被打满了那就抓一个 dump 看看到底咋回事。 二&#xff1a;为什么会打…

【雕爷学编程】Arduino动手做(181)---Maixduino AI开发板11

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

中断管理

其实&#xff0c;关于中断的概念和定义在之前已经反复学习和实践了&#xff0c;这节主要讲和FreeRTOS相关的中断知识。 中断优先级 任何中断的优先级都大于任务&#xff01; 在我们的操作系统&#xff0c;中断同样是具有优先级的&#xff0c;并且我们也可以设置它的优先级&a…

汽车EBSE测试流程分析(四):反思证据及当前问题解决

EBSE专题连载共分为“五个”篇章。此文为该连载系列的“第四”篇章&#xff0c;在之前的“篇章&#xff08;三&#xff09;”中已经结合具体研究实践阐述了“步骤二&#xff0c;通过系统调研确定改进方案”等内容。那么&#xff0c;在本篇章&#xff08;四&#xff09;中&#…

elementUI全屏loading的使用(白屏的解决方案)

官网中有使用方法&#xff0c;但是我实际上手之后会出现白屏&#xff0c;解决办法如下&#xff1a; <el-button type"text" size"small" click"delRow(scope)"> 删除</el-button>loading: false, // loading 动画loadingInstance…

windows图标白了,刷新图标

1.进入C盘&#xff0c;user(用户文件夹)&#xff0c;进入当前用户文件夹&#xff0c;再进入隐藏文件夹(AppDada)&#xff0c;最后进入Local 2.删除Local文件夹里的IconCache.db文件 3.重启资源管理器 -------------------------------------------- 或者创建bat文件&#xf…

爬虫008_流程控制语句_if_if else_elif_for---python工作笔记026

然后我们再来看一下这里的,判断,可以看到 再看一个判断,这里的布尔类型 第二行有4个空格,python的格式 注意这里,输入的age是字符串,需要转一下才行 int可以写到int(intput("阿斯顿法师打发地方")) 这样也可以

无涯教程-Perl - Subroutines(子例程)

定义子程序 Perl编程语言中 Subroutine子程序定义的一般形式如下: sub subroutine_name {body of the subroutine } 调用该Perl Subroutine的典型方式如下- subroutine_name( list of arguments ); 在Perl 5.0之前的版本中&#xff0c;调用 Subroutine的语法略有不同&…

认识Webpack插件Plugin;CleanWebpackPlugin插件;HtmlWebpackPlugin;DefinePlugin;Mode模式

目录 1_认识插件Plugin2_CleanWebpackPlugin3_HtmlWebpackPlugin4_DefinePlugin4.1_介绍4.2_DefinePlugin的使用 5_Mode模式 1_认识插件Plugin Webpack的另一个核心是Plugin&#xff0c;官方有这样一段对Plugin的描述&#xff1a; While loaders are used to transform certai…

运算放大器(二):恒流源

一、实现原理 恒流源的输出电流能够在一定范围内保持稳定&#xff0c;不会随负载的变化而变化。 通过运放&#xff0c;将输入的电压信号转换成满足一定关系的电流信号&#xff0c;转换后的电流相当一个输出可调的简易恒流源。 二、电路结构 常用的恒流源电路如…

HCIP期中实验

考试需求 1 、该拓扑为公司网络&#xff0c;其中包括公司总部、公司分部以及公司骨干网&#xff0c;不包含运营商公网部分。 2 、设备名称均使用拓扑上名称改名&#xff0c;并且区分大小写。 3 、整张拓扑均使用私网地址进行配置。 4 、整张网络中&#xff0c;运行 OSPF 协议…

Jenkins工具系列 —— 插件 钉钉发送消息

文章目录 安装插件 Ding TalkJenkins 配置钉钉机器人钉钉APP配置项目中启动钉钉通知功能 安装插件 Ding Talk 点击 左侧的 Manage Jenkins —> Plugins ——> 左侧的 Available plugins Jenkins 配置钉钉机器人 点击 左侧的 Manage Jenkins &#xff0c;拉到最后 钉…