iOS-开辟子线程(NSThread、NSOperationQueue、GCD)

2023-05-16

本节主要总结一些开辟子线程的常用的几种方法。

一、通过NSThread类开辟子线程

1.NSThread手动开启子线程

// 创建线程对象.
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(eat) object:nil];
thread.name = @"a";

// 手动开启线程
[thread start];
// 手动取消线程
[thread cancel]

2.NSThread自动开启子线程

    // 创建线程对象自动开启,无返回值
    [NSThread detachNewThreadSelector:@selector(eat) toTarget:self withObject:nil];

除了创建启动外,NSThread 还以很多方法,更多方法可以去NSThread.h类的定义里去看,写的很详细。

3.子线程中执行完之后,回到主线程:

[self performSelectorOnMainThread:@selector(mainQueue) withObject:nil waitUntilDone:NO];

二、通过NSOperationQueue类开辟子线程

1.Queue (队列)中是 NSOperation 对象

一个 NSOperation 对象就是一个任务(一段功能代码),本身和线程没有关系(不能开子线程)。
NSOperation 只是一个抽象类,所以不能封装任务。但它有 2 个子类用于封装任务。分别是:NSInvocationOperation 和 NSBlockOperation 。创建一个 Operation 后,需要调用 start 方法来启动任务,它会默认在当前队列同步执行。当然你也可以在中途取消一个任务,只需要调用其 cancel 方法即可。这种方式任务都是在主线程中执行。

    // NSInvocationOperation 创建任务对象(多个)
    NSInvocationOperation *invocation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(eat) object:nil];
    NSInvocationOperation *invocation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(eat) object:nil];


    // NSBlockOperation 创建任务对象
    __weak typeof(self) weakSelf = self;
    //__weak ViewController *weakSelf = self;
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        [weakSelf eat]; // 任务1
    }];
    // addExecutionBlock会创建一个新的线程来执行任务,而不是在同一个子线程中。   
    [blockOperation addExecutionBlock:^{
        [weakSelf eat]; // 任务2
    }];
    [blockOperation addExecutionBlock:^{
        [weakSelf eat]; // 任务3
    }];

    // 开启线程
    [invocation1 start];
    [invocation2 start];
    [blockOperation1 start];

也可以直接添加进队列 NSOperationQueue 里面,任务会自动执行,不需要你再手动调用start方法。NSOperation 和 NSOperationQueue 类似于 GCD 的 任务队列这种方式任务都是在子线程中执行。

    // 创建 operationQueue 队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    //NSOperationQueue 有一个属性 maxConcurrentOperationCount 最大并发数,用来设置最多可以让多少个任务同时执行。当你把它设置为 1 的时候,变为串行队列!

    queue.maxConcurrentOperationCount = 1;

    [queue addOperation:invocation1];
    [queue addOperation:invocation2];

    [queue addOperation:blockOperation];

NSOperation 取消任务调用

    [invocation1 cancel];
    [blockOperation cancel];

2.子线程中执行完之后,回到主线程:

   [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [weakSelf mainQueue];
    }];

NSOperation 还有好多属性方法,具体的可以NSOperation.h里面查看,会找到你需要的方法属性。

3.任务执行完成后回调

 [blockOperation setCompletionBlock:^{
     // 任务都执行完后回调
 }];

三、通过GCD开辟子线程

在 GCD 中,加入了两个非常重要的概念: 任务队列
任务:即操作,你想要干什么,说白了就是一段代码,在 GCD 中就是一个 Block,所以添加任务十分方便。任务有两种执行方式: 同步执行异步执行,他们之间的区别在于 是否会创建新的线程

1.GCD 核心: 队列和任务
2.队列: 串行队列和并行队列.
3.任务: block块 和 函数
4.程序本身有一个串行队列(主线程).
5.有4个并行队列(优先级区别).
6.自定义一个串行队列
7.自定义并行队列.

同步和异步区别:同步(sync) 和 异步(async) 的主要区别在于会不会阻塞当前线程,直到 Block 中的任务执行完毕! 如果是同步(sync) 操作,它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行。 如果是异步(async)操作,当前线程会直接往下执行,它不会阻塞当前线程。

队列:用于存放任务。一共有两种队列, 串行队列 和 并行队列。

串行队列: 放到串行队列的任务,GCD 会 FIFO(先进先出) 地取出来一个,执行一个,然后取下一个,这样一个一个的执行。

并行队列: 放到并行队列的任务,GCD 也会 FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。这样由于取的动作很快,忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD 会根据系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。

任何需要刷新 UI 的工作都要在主队列执行,所以一般耗时的任务都要放到别的线程执行。

它自己可以创建 串行队列, 也可以创建 并行队列.它有两个参数,第一个参数是标识符,用于 DEBUG 的时候标识唯一的队列,可以为空。第二个才是最重要的。第二个参数用来表示创建的队列是串行的还是并行的,传入DISPATCH_QUEUE_SERIAL 或 NULL 表示创建串行队列。传入 DISPATCH_QUEUE_CONCURRENT 表示创建并行队列。

//***************** 自定义串行队列 *****************//
// 串行队列 SERIAL
dispatch_queue_t queue = dispatch_queue_create("SERIAL", DISPATCH_QUEUE_SERIAL);

    // 防止循环引用 使用 __weak 修饰
    __weak typeof(self)weakSelf = self;
    // 异步任务 分线程中执行 不会阻塞主线程
    dispatch_async(queue, ^{
        [weakSelf eat];
    });
   // 同步任务 主线程中执行 会阻塞当前线程
    dispatch_sync(queue, ^{        
        [weakSelf eat];
    });
    //***************** 自定义并行队列 *****************//
    // 并行队列 CONCURRENT
    dispatch_queue_t queue2 = dispatch_queue_create("CONCURRENT", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue2, ^{
        [weakSelf eat];
    });
    dispatch_sync(queue2, ^{
        [weakSelf eat];
    });
//***************** 获取系统的4个并行队列 ****************//
    dispatch_queue_t queue4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue4, ^{
        [weakSelf eat];
    });

解决异步情况下抢占资源问题,可以加锁NSLock。获取资源时lock住,获取完资源后unlock。或者用@synchronized包裹获取资源代码。

1.子线程中执行完之后,回到主线程:

dispatch_async(dispatch_get_main_queue(), ^{
     // 更新UI
});

2.所有任务执行完成后回调

   dispatch_group_t group = dispatch_group_create();
   dispatch_group_async(group,queue2,^{执行任务A});
   dispatch_group_async(group,queue2,^{执行任务B});
   dispatch_group_async(group,queue2,^{执行任务C});
   dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       NSLog(@"queue2的所有任务都执行完了");
   });

借鉴博客:
http://www.jianshu.com/p/0b0d9b1f1f19
http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
http://guimingsu.com/blog/2015/12/22/thread/

总结

1.无论使用哪种方法进行多线程开发,每个线程启动后并不一定立即执行相应的操作,具体什么时候由系统调度,CPU空闲时就会执行。

2.NSBlockOperation 添加任务 addExecutionBlock 任务全部完成时回调方法 setCompletionBlock;GCD任务全部完成时回调方法 dispatch_group_notify。

3.NSThread适合轻量级多线程开发,要手动控制线程,线程总数无法控制,无法线程依赖。

4.对于简单的多线程开发建议使用NSObject的扩展方法完成,而不必使用NSThread。

5.可以使用NSThread的currentThread方法取得当前线程,使用 sleepForTimeInterval:方法让当前线程休眠。

6.NSOperation进行多线程开发可以控制线程总数及线程依赖关系。

7.创建一个NSOperation不应该直接调用start方法, 如果直接start则会在主线程中调用, 而是应该放到NSOperationQueue中启动。

8.相比NSInvocationOperation推荐使用NSBlockOperation,代码简单,同时由于闭包性使它没有传参问题。

9.NSOperation是对GCD面向对象的封装,但是GCD基于C语言开发,效率却更高. 建议如果任务之间有依赖关系或者想要监听任务完成状态的情况下优先选择NSOperation否则使用GCD。

10.在GCD中串行队列中的任务被安排到一个单一的子线程中,可以方便地控制执行顺序;并发队列在多个线程中执行(前提是使用异步方法),顺序控制相对复杂,但是更高效。

11.在GDC中一个操作是多线程执行还是单线程执行取决于当前队列类型和执行方法,只有队列类型为并发队列并且使用异步方法执行时才能在多个线程中并发执行。

12.相比使用NSLock,@synchronized更加简单。

13.主队列是个串行队列,但是它不会新建子线程,所以当在主队列中执行dispatch_sync 时会卡死。

Demo下载地址https://github.com/MichaelSSY/ThreadTest

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS-开辟子线程(NSThread、NSOperationQueue、GCD) 的相关文章

  • OpenStack-Ceilometer项目功能与架构介绍

    1 OpenStack Ceilometer项目简介 OpenStack通过Telemetry项目提供计量与监控服务 xff0c 该项目旨在针对组成已部署云的物理和虚拟资源 xff0c 可靠地收集并保存各类使用数据 xff0c 以便对这些数
  • redhat 查询端口占用

    linux redhat 端口和服务的查看与终止 redhat 查询端口占用情况和杀死占用的服务 netstat anp grep lt 端口号或者程序名称 gt 查8080端口占用情况 netstat anp grep 8080 查jav
  • mapl

    business card case executive veto power anime stream radio johann strauss black layout pink star job interview question
  • Knowledge Tracing: A Survey阅读笔记

    xff08 注 xff1a 为了方便后续阅读KT论文 xff0c 文中一些名词使用英文 文中保留的序号与原论文参考文献一致 行文会在后续反刍过程中改进 xff09 原文链接 xff1a https arxiv org abs 2201 06
  • iOS_NSAttributedString 的21种属性详细介绍(图文混排)

    说明 NSAttributedString 可以非常方便的实现文字排版和图文混排功能 共有21中效果 API 本文将较详细的介绍21种的属性的使用 注 本博客由 64 凡俊编写 64 Scott 64 春雨 审核 若转载此文章 请注明出处和
  • ++和--的用法

    单独使用时 xff0c 43 43 或者 无论是放在变量的前面还是后面 xff0c 结果是一样的 参与操作时 如果 43 43 或者 在变量的后面 xff0c 先拿变量参与操作 xff0c 后变量做 43 43 或者 例如 int a 61
  • geoserver集群

    软件准备 geoservertomcat 插件 下载地址 xff1a https build geoserver org geoserver activeMQ broker plugin zipjms cluster plugin zip
  • 为moment.js正名

    说来也奇怪 xff0c 总有人在耳边说moment js对国际化日期支持不好 xff0c 坚决不要使用 xff0c 会带来很多问题之类的话 但就我个人经验来看 xff0c 还从未见到过任何一个反例 xff0c 恰好我又是个不信邪的人 xff
  • sqlite3 的二进制数据插入与获取

    sqlite3 存储和查找二进制数据对象 使用c语言接口 思路 xff1a 通过让代码执行执行sql语句进行查找 xff0c 但二进制的显示方法无法确定所以 xff0c 二进制数据对象查询语句略有不同 注意 xff1a sqlite3的数据
  • MySQL修改数据表中的字段名

    MySQL修改数据表中的字段名 在一张数据表中只能设置一个唯一名称的字段名 在同一张数据表中 xff0c 不能出现两个名称完全相同的字段名 因此 xff0c 数据库系统可以通过字段名来区分数据表中的不同字段 在MySQL中 xff0c AL
  • 证明:当gcd(a, b) = 1,则gcd(a + b, a) = 1

    假设 xff1a gcd a b 61 1 证明 xff1a gcd a 43 b b 61 1 反证法 xff1a 假设gcd a 43 b b 61 k 61 1 则 xff1a b 61 k r1 a 43 b 61 a 43 k r
  • 实战microPython(二)时钟和日历的使用

    实战microPython 2 时钟和日历的使用 David Zou 创客DIY乐园 对于一名创客 xff0c 自制一个个性化的时钟或闹钟啥的 xff0c 应该是比较常见的入门级任务了 通常我们制作时钟或闹钟的时候 xff0c 都需要借助专
  • 实战microPython(10)-蓝牙模块的使用

    实战microPython 10 蓝牙模块的使用 David Zou 2018 12 27 本文讲解蓝牙模块的使用 xff0c 以及通过uPyBoard来操作蓝牙模块并实现手机和uPyboard互动 正在学习和使用uPyBoard开发的小伙
  • nohup: failed to run command ‘java’: No such file or directory解决方案

    场景 xff1a Jekins实现自动化部署 问题描述 Jekins打包后端项目后发送Jar到对应的应用服务器 xff0c 通过应用服务器Shell脚本启动服务报错 nohup span class token operator span
  • System.DllNotFoundException: 无法加载DLL

    问题描述 使用VS2005在Windows Server 2003上编译C 43 43 代码 输出dll文件 把该dll放到运行机器 与编译机器的系统完全一致 上 供C 代码 web前台 调用 提示无法加载dll 分析 1 路径完全没有问题
  • Flutter学习(一)

    开始学习 为什么使用 Fultter xff1f 为什么使用 Fultter xff1f 言归正传 xff0c 亘古不变环境搭建 xff1a Android Studio 下载 xff0c 我的是这个版本 xff0c emmmm 首先安装g
  • 树莓派 ubuntu18.04 mate更换为国内镜像源

    文章目录 前言正文参考 前言 我使用树莓派3B 43 xff0c 烧写的操作系统为ubuntu mate18 04 网上的相关教程很多 xff0c 但说的很详细的不多 xff0c 本文算是做一个简单的整理 树莓派采用的是arm架构 xff0
  • Spring常见面试题总结(超详细回答)

    1 Spring是什么 Spring是一个轻量级的IoC和AOP容器框架 是为Java应用程序提供基础性服务的一套框架 xff0c 目的是用于简化企业应用程序的开发 xff0c 它使得开发者只需要关心业务需求 主要包括以下七个模块 xff1
  • 在android studio中使用kotlin

    一 安装kotlin插件 二 导入Kotlin的核心库及其扩展库Anko库 1 在项目根目录下的build gradle文件中指定kotlin插件的版本及路径 buildscript ext kotlin version 61 span c
  • sublime Text SFTP LICENSE 注册码

    34 email 34 34 xiaosong 64 xiaosong me 34 34 product key 34 34 d419f6 de89e9 0aae59 2acea1 07f92a 34 这个就是SFTP注册码 将上面的代码复

随机推荐

  • Aliddns插件使用:小白超详细图文教程

    Aliddns插件使用 xff1a 小白超详细图文教程 Aliddns插件 xff0c 用阿里的云解析速度是快 xff0c 天下武功为快不破 作为一个小白的我 xff0c 看这篇帖子也是一脸懵逼 xff0c http koolshare c
  • 在PyQt5中使用多进程(multiprocessing)

    multiprocessing对象要放在 main 所在的启动文件使用槽连接multiprocessing对象 import sys from multiprocessing import Pool from PyQt5 QtWidgets
  • go使用exec执行命令

    golang exec 命令执行
  • 【汇编】AT89C52点亮一盏LED灯(汇编语言)

    学习利用汇编语言写单片机程序的第一步是要学习汇编语言的相关理论知识 xff0c 那么实践操作的第一步肯定是从点灯开始啦 xff01 编译环境 xff1a keil4 编译语言 xff1a 汇编语言 内容 xff1a 一 keil4建立AT8
  • wsl-常见问题

    基于wsl2的docker如何迁移镜像文件 默认基于wsl2的docker desktop的镜像是有wsl2管理的 xff0c 而wsl2一般在c盘 当下载的镜像多了之后 xff0c 就会把C盘爆满 wsl shutdown wsl exp
  • 求0-7所组成的奇数个数

    include lt stdio h gt include lt stdlib h gt main long sum 61 4 long s 61 4 int j for j 61 2 j lt 61 8 j 43 43 printf 34
  • UITabBarController详解

    一 UITabBarController简介 一 继承关系 UITabBarController和UINavigationController类似 xff0c 也继承于UIViewController xff0c 也可以轻松地管理多个控制器
  • 关于多卡Android设备获取手机号的研究

    首先我们都知道如何获取Android手机的Sim手机号 fun getNativePhoneNumber context Context String val tm 61 context getSystemService Context T
  • 【入门学习三】基于 FPGA 使用 Verilog 实现按键状态机代码及原理讲解

    目录 一 状态机二 模块设计三 代码实现四 管脚配置及结果展示 上一篇博文 xff1a 入门学习二 基于 FPGA 使用 Verilog 实现蜂鸣器响动的代码及原理讲解 概述 xff1a 前面的两篇文章 xff0c 其中按键模块采用的是延时
  • 【二分】洛谷_3902 递增

    题意 给出n个数 xff0c 求出修改最少的数字 xff0c 使得数列严格单调递增 思路 我们用一个数组s来记录当前存到的数字 xff0c 每次放进一个数字 xff0c 我们就判断它是不是比之前的数小 xff0c 否则我们就二分找到一个最好
  • 使用mysql语句进行分组查询

    使用mysql语句进行分组查询 1 作用 对整个数据表的某几个字段进行分组 然后通过分组函数得到我们想要的结果 2 如何用 2 1 只分一个组 2 1 1 本质 就是根据分组字段把整个表的数据分为几组 然后分别对每组里面的数据进行汇总查询或
  • 删除流氓软件 Alibaba PC Safe Service

    好久没用笔记本了 xff0c 之前被人再用 xff0c 今天偶尔想用下 xff0c 开机遇到两件恶心到家的事情 xff0c 第一件 xff0c 360弹窗 xff0c 不停的显示 xff0c 感觉特别烦就卸载了360所有的东西 xff1b
  • 枚举类型字符化输出的方法

    方法一 xff1a 用函数 xff08 简单的应用场景下 xff09 方法二 xff1a 用字符串转换 创建枚举类型 enum athleteName WANG ZHOU SU CHO KIM LI MIRE BOUTIN 创建结构体 st
  • 【单片机竞赛:共阳数码管静态控制】

    51单片机 xff08 国信天长单片机实训平台 xff09 基于keli的常用程序之 共阳数码管一般静态控制程序设计 文章目录 51单片机 xff08 国信天长单片机实训平台 xff09 基于keli的常用程序之 共阳数码管一般静态控制程序
  • Linux - 搭建LDAP统一认证服务

    目的 通过以下步骤最终可使用ldap server中的用户登录一台ldap client xff0c 并允许有sudo权限 平常公司中所用的域账号以及服务器账号也许就是使用如下方式 xff0c 但是应该没有这么简陋 xff0c 只是借机了解
  • 每日练习------有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数

    题目 有n个整数 xff0c 使其前面各数顺序向后移m个位置 xff0c 最后m个数变成最前面的m个数 解题关键 需要新建个数组使得原数组可以做到整体往后移动M位 思路 1 创建个有n个整数的数组 2 输出n个数字 存储到数组中 3 遍历原
  • FLTK-Rs

    终于还是到这一步了 xff0c 可视化 xff0c 我的超人 xff01 FLTK是一个跨平台的轻量级 gui 库 该库本身是用 C 43 43 98 编写的 xff0c 具有很高的可移植性 fltk crate 是用 Rust 编写的 x
  • STM32寄存器点灯失败

    include 34 stm32f10x h 34 int main void unsigned int 0x40021018 61 1 lt lt 3 打开时钟GPIOB unsigned int 0x40010C00 61 1 lt l
  • nested exception is java.sql.SQLException: com.mysql.cj.jdbc.Driver

    解决方案 在 pom xml 中 xff0c 加入 mysql 的 maven 引用 span class token tag span class token tag span class token punctuation lt spa
  • iOS-开辟子线程(NSThread、NSOperationQueue、GCD)

    本节主要总结一些开辟子线程的常用的几种方法 一 通过NSThread类开辟子线程 1 NSThread手动开启子线程 span class hljs comment 创建线程对象 span span class hljs built in