【iOS】多线程基础

文章目录

进程与线程

进程:具有一定独立功能的程序,是系统进行资源分配与调度的一个独立单位,也就是说进程是可以独立运行的一段程序

  • 进程的特点:
    1. 动态性:进程是程序的一次执行过程,是动态产生,动态消亡的
    2. 独立性:每个进程都有自己的地址空间,数据栈等运行环境,互不影响
    3. 并发性:多个进程可以同时存在于内存中,并发执行(宏观并行,微观串行)
    4. 拥有资源:如内存,文件描述,网络端口,线程本身不拥有这些,而共享同一进程的资源

线程:是进程的一个实体,是cpu调度和分派的基本单位,比进程更小的能独立运行的基本单位

  • 线程的特点:
    1. 轻量级:创建,销毁线程的开销比进程小得多
    2. 共享资源:多线程共享进程的内存(堆、静态区、文件描述符)每个线程有自己 独立的栈等资源
    3. 同步问题:因为共享资源,所以同步问题会比较复杂,锁,信号量,原子操作等

二者的关系

  1. 一个线程只能属于一个进程,而一个进程可以有多个线程,至少会有一个主线程的存在

在iOS中UI的更新必须在主线程

  1. 资源分配给进程,同一进程的所有线程共享该进程的所有资源
  2. 不同进程的线程之间要利用消息通信的方法实现同步
  3. 线程是进程内的一
    个执行单元,真正在cpu上运行的是线程

如图在活动检测器上我们能看到一个软件就是一个进程,然后同一个进程会有很多个线程

线程作为调度与分配的基本单位,进程作为拥有资源的基本单位

不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行

进程是拥有资源的一个独立单位,线程不拥有系统资源,但是可以访问属于进程的资源

进程是资源分配的最小单位,线程是cpu调度的基本单位

多线程

同一时间内,单核cpu只能处理一条线程,而我们的多线程执行的时候就是cpu在多线程之间快速进行切换操作,而现在我们使用的基本都是多核cpu,比如我们经常听到的四核八核之类的,而随着多核的使用,多线程并行也可以实现

优缺点

优点

  1. 可充分利用多核cpu提升效率
  2. 提升程序的响应速度,避免主线程的卡顿
  3. 改善程序结构,实现任务分工
  4. 后台处理能力强,如图片音视频等的解码

缺点

  1. 多个线程共享同一块内存时,会出现线程安全问题,如重复读写,数据竞争或者崩溃等

可通过加锁,串行队列,信号量等问题解决,但是又会影响性能

  1. 上下文切换成本高,大量线程并不代表快,有时反而更慢
  2. 创建和销毁线程需要成本
  3. 线程太多可能导致系统资源耗尽
  4. 调试难度变大

线程的状态

线程五大状态:创建 → 就绪 → 运行 → 阻塞 → 终止

  1. 创建:只是创建了线程对象,线程本体未进入系统调度队列

    objc 复制代码
    NSThread *t = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];

    仅分配结构,线程未开始进行

  2. 就绪:调用了start方法,线程会被加入可调度线程池,等待 CPU 调度,调用 start 不会立即执行线程,它只是进入"可执行但未执行"的状态

  3. 运行:CPU 调度到该线程时,线程开始执行,CPU 可能随时让线程暂停,线程可能频繁在 就绪和运行之间切换,开发者无法控制什么时候切换

  4. 阻塞:线程 主动或被动暂停执行,暂时不具备继续运行的条件,如:

    • 休眠(sleep):NSThread 提供两种方法

      sleepForTimeInterval:休眠指定时长

      objc 复制代码
      [NSThread sleepForTimeInterval:2]; //休眠2秒

      sleepUntilDate:休眠到指定时间

      objc 复制代码
      [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
    • 同步锁阻塞:

      使用 @synchronized(obj) 时:如果锁被其他线程占用,当前线程会被阻塞,等待拿到锁再继续执行

  5. 死亡:线程结束执行,有两种情况,分别是正常终止和非正常终止

    • 正常终止,即线程执行完毕
    • 非正常终止,即当满足某个条件后,在线程内部(或者主线程中)终止执行(调用exit方法等退出)

多线程实现方案

NSThread

属于手动管理线程方案,需要自己创建,启动,控制

创建线程的三种方式
  1. alloc + init + start

    objc 复制代码
    NSThread *t = [[NSThread alloc] initWithTarget:self selector:@selector(runTask) object:nil];
    [t start];
  2. detachNewThreadSelector

    objc 复制代码
    [NSThread detachNewThreadSelector:@selector(runTask) toTarget:self withObject:nil];
  3. block

    objc 复制代码
    [NSThread detachNewThreadWithBlock:^{
        [self runTask];
    }];

休眠的方法在上面已经给出

线程取消(不会立即停止)
objc 复制代码
[t cancel];
if ([NSThread currentThread].isCancelled) return;
特点
  • 手动创建线程可控性高
  • 需要自己管理生命周期麻烦
  • 不会自动管理任务依赖、队列组合
  • 适合简单线程任务场景

同步、异步、串行、并行

同步 sync
  • 不会开新线程
  • 阻塞当前线程(必须等任务执行完才往下走)
异步 async
  • 可能会开新线程(并不是一定开,看队列类型)
  • 不会阻塞当前线程
串行队列 serial
  • 一次只执行 一个任务
  • 任务 按顺序 执行
并发队列 concurrent
  • 可以同时执行多个任务

  • 任务可能无序、重叠执行

组合 是否开线程 是否并发 是否阻塞当前线程 顺序
sync + serial
sync + concurrent
async + serial 是(1 条)
async + concurrent 是(多条)

GCD

GCD 是 iOS 多线程的核心,特点是:自动管理线程池,自动管理任务调度,性能最佳

创建队列

串行队列

objc 复制代码
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);

并行队列

objc 复制代码
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);

全局队列

objc 复制代码
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
简单使用
objc 复制代码
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    // 子线程执行耗时操作
    dispatch_async(dispatch_get_main_queue(), ^{
        // 主线程更新 UI
    });
});
dispatch_after(延迟执行)
objc 复制代码
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    NSLog(@"延迟2秒执行");
});
dispatch_once(单例实现)
objc 复制代码
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只执行一次
});
dispatch_group(任务组)

等待多个线程执行完,再执行某个操作:

objc 复制代码
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
    // 任务1
});
dispatch_group_async(group, queue, ^{
    // 任务2
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 所有任务执行结束
});
dispatch_semaphore(信号量)

这里我看别人博客的理解,信号量就是一个类似于保安大爷的东西,他会确保你同时可以执行的线程数量,可以保持线程同步,将异步任务转化为同步执行任务并且保障线程的安全,为线程加锁

objc 复制代码
dispatch_semaphore_t sema = dispatch_semaphore_create(1);

dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
// 访问临界区
dispatch_semaphore_signal(sema);
特点
  • 自动线程管理 即不需要创建线程对象
  • 性能最好
  • API 强大(barrier、group、after、once、semaphore)
  • 适合绝大多数多线程任务

NSOperation(比 GCD 更面向对象,功能最强)

NSOperation 是基于 GCD 封装的 面向对象多线程框架

比 GCD 更高级:支持 任务依赖 ;支持 任务暂停/取消 ;支持 最大并发数限制 ;支持 任务队列管理(NSOperationQueue)

使用方式

一、创建Operation

  1. NSInvocationOperation(调用方法)
objc 复制代码
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(runTask) object:nil];
  1. NSBlockOperation(执行 block)
objc 复制代码
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"任务1");
}];

[op addExecutionBlock:^{
    NSLog(@"任务2");
}];
  1. 自定义子类(最强方式)

    重写 main 方法:

    objc 复制代码
    @interface MyOperation : NSOperation
    @end
    
    @implementation MyOperation
    - (void)main {
        @autoreleasepool {
            // 线程任务
        }
    }
    @end

二、把 Operation 加到队列执行

NSOperationQueue(自动开启线程)

objc 复制代码
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op];

NSOperationQueue 会自动创建线程并执行任务

队列类型
  • 主队列(UI 更新)
  • 自定义队列(默认并发)
objc 复制代码
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 2; // 可限制并发数
其他功能
  1. 添加依赖

    一个任务依赖另一个任务执行完:

    objc 复制代码
    [op2 addDependency:op1]; // op1 执行完,op2 才执行
  2. 取消操作

    objc 复制代码
    [op cancel];
  3. 暂停/继续队列

    objc 复制代码
    queue.suspended = YES; // 暂停
    queue.suspended = NO;  // 继续
  4. 最大并发数

    objc 复制代码
    queue.maxConcurrentOperationCount = 1; // 串行
    queue.maxConcurrentOperationCount = 5; // 并发
特点
  • 基于 GCD,更面向对象
  • 支持依赖、优先级、取消、暂停、最大并发数
  • 更适合复杂任务控制

总结

多线程的使用是十分重要的知识点,在后面具体项目中与其他第三方库结合的使用也很多,还需要继续学习并加以实践)

相关推荐
2501_915909063 小时前
iOS APP 抓包全流程解析,HTTPS 调试、网络协议分析与多工具组合方案
android·ios·小程序·https·uni-app·iphone·webview
2501_915106323 小时前
游戏上架 App Store 的技术流程解析 从构建到审核的全流程指南
游戏·macos·ios·小程序·uni-app·cocoa·iphone
非专业程序员3 小时前
精读 GitHub - servo 浏览器(一)
前端·ios·rust
laocaibulao5 小时前
mac电脑brew update很慢咋办?
macos
心灵宝贝5 小时前
XMind for Mac v24.01.dmg 安装教程(Mac思维导图软件下载安装步骤)
macos·xmind
醇氧15 小时前
Mac 安装 Docker Desktop
macos·docker·容器
2501_9160074716 小时前
iOS 压力测试的工程化体系,构建高强度、多维度、跨工具协同的真实负载测试流程
android·ios·小程序·uni-app·cocoa·压力测试·iphone
神秘人-解说17 小时前
在Mac上安装Windows 11/10双系统(M1/M2/Intel通用)
windows·macos·mac安装双系统·mac安装虚拟机·mac安装windows
知难行难17 小时前
macOS配置Apocrita及ssh访问及获取GPU权限
运维·macos·ssh