01-iOS多线程 | 进程,线程,cpu与线程,生命周期,线程池,线程锁,线程与RunLoop

118 阅读4分钟

大神链接

进程
  • 进程是系统中正在运行的一个程序,是程序执行时的实例
    • 程序运行时系统就会创建一个一个进程
    • 然后把进程放入进程就绪队列
    • 进程调度器选择进程就会分配CPU时间,程序开始真正运行
  • 每个进程都是相互独立的
    • 每个进程运行在其专有且受保护的内存空间内
截屏2022-09-01 10.28.20.png
线程
  • 线程是进程的基本执行单元,一个进程的所有任务都是在线程中执行的
    • 进程要执行任务必须要有线程,一个进程至少有一个线程
    • 程序启动时会默认开启一个线程(即主线程或UI线程)
进程与线程
  • 进程是资源分配的最小单元,线程是程序执行的最小单元
  • 进程有自己的独立地址空间
    • 每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段,堆栈段和数据段
  • 线程共享进程中的数据
    • 不同线程使用相同的地址空间(都是同一个进程的内存资源)
    • CPU切换一个线程的花费要比进程小很多
    • 创建一个线程的开销也比进程小很多
  • 线程之间的通信更方便
    • 同一进程下的线程共享全局变量,静态变量等数据
    • 而进程之间的通信需要以通信的方式(IPC)进行
  • 多进程使程序更健壮
    • 多线程程序只要一个线程死掉,整个进程也死掉
    • 而一个进程死掉并不会对另一个进程造成影响,每个进程都有自己的独立地址空间
    • iOS 只支持每个应用都是单进程(Android支持一个应用开启多个进程)
多线程
NSLog(@"开始");
NSInteger count = 1000 * 100;
for (NSInteger i = 0; i < count; i++) {
    @autoreleasepool {
        // 栈区
        NSInteger num = i;
        // 常量区
        NSString *name = @"RENO";
        // 堆区
        NSString *myName = [NSString stringWithFormat:@"%@ - %ld",name,(long)num];
        NSLog(@"%@",myName);
    }
}
NSLog(@"结束");

如上代码,循环大量数据,都在主线程一个线程中执行很耗时,造成卡顿等,影响用户操作,开启多个线程将一个事物分成多个小事物执行,可以节省时间,不会影响用户体验

多线程优缺点
  • 优点:
    • 提高程序执行效率
    • 提高CPU,内存等利用率
    • 线程上的任务执行完后,线程自动销毁
  • 缺点:
    • 开启一个线程会占用一定的内存空间(一个线程默认时512KB)
    • 开启大量的线程会占用大量的内存空间,降低程序性能
    • 线程越多,CPU在线程上的开销越大
CPU与线程
  • 时间片是操作系统分配给每个正在运行的进程上一段CPU时间(在抢占内核中是从进程开始运行直到被抢占的时间)

每个进程被分配一段CPU时间段,每次分配的时间段可能在执行同一个线程的任务也有可能是不同线程的任务

单核CPU
  • 多线程的本质:
    • 多线程的执行是CPU快速在多个线程之间进行切换
    • 如果线程过多,CPU会在多个线程之间切换,消耗大量的CPU资源,反而导致执行效率降低
  • 多线程同时执行:
    • 多线程同时执行是CPU快速在多个线程之间切换
    • CPU调度线程的时间很快,就造成多线程同时执行的现象
    • 多线程并发不是真正的同时执行,而是多条线程之间的快速切换
  • 如果线程非常多,CPU会在多个线程之间切换
    • 消耗大量CPU资源
    • 每个线程被调度的次数会降低
    • 线程的执行效率降低
多核CPU
  • 不同CPU分配计算机资源给多线程
    • 同样线程数量过多,多核CPU也可能出现每个CPU快速在多个线程之间切换的现象,消耗大量CPU资源,导致执行效率降低
线程的生命周期
  • NEW-新建
    • 创建一个新线程
  • Runable-就绪
    • 线程创建之后,在调用开始之前,线程处于等待状态,等待CPU时间分配执行
  • Runing-运行
    • 就绪状态的线程被调用获得CPU资源,进入运行状态
  • Block-阻塞
    • 在运行状态,因为一些原因变成阻塞状态
      • sleep休眠,同步锁
      • 线程就从可调度线程池中移除,处于阻塞状态
    • 当sleep结束,获取同步锁,此时会添加到可调度线程池,线程被重新唤醒
      • 唤醒的线程不会立即执行,会再次等待CPU分配资源进入运行状态
  • Dead-销毁
    • 如果线程正常执行完毕后或线程提前强制终止或出现异常导致结束,那么线程就要被销毁,释放资源

线程生命周期.png

线程与锁

1.自旋锁

自旋锁是一种用于保护多线程共享资源的锁

  • 当自旋锁尝试获取锁时,以忙等(busy waiting)的形式不断循环检查锁是否可用
  • 当上一个线程的任务还没执行完毕时,下一个线程会一直等待(不会休眠)
  • 当上一个线程任务执行完毕,下一个线程会立即执行

多核CPU下,对持有锁时间短的程序来说,使用自旋锁代替互斥锁能够提高程序性能 iOS的自旋锁: OSSpinLock,dispatch_semaphore_t

2.互斥锁

  • 当上一个线程的任务还没有执行完毕时
  • 下一个要执行任务的线程进入休眠状态等待任务执行完毕
  • 上一个线程的任务执行完毕,下一个线程会自动唤醒然后执行任务,该任务不会立即执行,而是变为就绪状态,重新抢占执行,被CPU分配调度执行

iOS互斥锁: pthread_mutex,@synchronized,NSLock,NSConditionLock,NSCondition,NSRecursiveLock

RunLoop与线程
  • RunLoop与线程是一一对应
    • 一个RunLoop对应一个线程
    • 对于主线程,RunLoop在程序启动就默认创建
    • 子线程,需要获取RunLoop才会被创建
    • 线程结束,RunLoop销毁