OC底层原理22-多线程

347 阅读4分钟

这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战

一.线程和进程

线程是进程的基本执⾏单元,⼀个进程的所有任务都在线程中执⾏

  • 进程想要执行任务,必须得有线程,进程至少要有一条线程
  • 程序启动默认开启一条线程,这条线程被称为主线程或UI线程 进程是指在系统中正在运行的一个应用程序
  • 每个进程之间是独立的,每个进程均运行在其专用的且受保护的内存空间内
  • 通过“活动监视器”可以查看Mac系统中所开启的进程

Xnip2021-08-07_20-24-57.jpg

mac有多进程 iOS没有多进程 有图标显示代表有系统桌面权限的

进程与线程的关系

地址空间:同⼀进程的线程共享本进程的地址空间,⽽进程之间则是独⽴的地址空间。

资源拥有:同⼀进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的

资源是独⽴的。

  1.  ⼀个进程崩溃后,在保护模式下不会对其他进程产⽣影响,但是⼀个线程崩溃整个进程都死掉。所以多进程要⽐多线程健壮。

  2. 进程切换时,消耗的资源⼤,效率⾼。所以涉及到频繁的切换时,使⽤线程要好于进程。同样如果要求同时进⾏并且⼜要共享某些变量的并发操作,只能⽤线程不能⽤进程

  3. 执⾏过程:每个独⽴的进程有⼀个程序运⾏的⼊⼝、顺序执⾏序列和程序⼊⼝。但是

  4. 线程不能独⽴执⾏,必须依存在应⽤程序中,由应⽤程序提供多个线程执⾏控制。

  5. 线程是处理器调度的基本单位,但是进程不是。

  6. 线程没有地址空间,线程包含在进程地址空间中

TLS:存储 例如

Xnip2021-08-07_20-34-03.jpg

Xnip2021-08-07_20-34-45.jpg

二.多线程原理

优点

  • 能适当提高程序的执行效率
  • 能适当提高资源的利用率
  • 线程上的任务执行完成后,线程会自动销毁 缺点
  • 开启线程需要占用一定的内存空间(默认情况下,每一个线程都占512KB)
  • 如果开启大量的线程,会占用大量的内存空间,降低程序的性能
  • 线程越多,CPU在调用线程上的开销就越大
  • 程序设计更加复杂,比如线程间的通信,多线程的数据共享

时间⽚的概念:CPU在多个任务直接进⾏快速的切换,这个时间间隔就是时间⽚ (单核CPU)同一时间,CPU只能处理1个线程 换言之,同一个时间只有1个线程在执行 多线程同时执行: 是CPU快速的在多线程之间的切换 CPU调度线程的时间足够快,就造成了多线程的“同时”执行的效果 如果线程数非常多 CPU会在N个线程之间切换,消耗大量的CPU资源 每个线程被调度的次数会降低,线程的执行效率降低

Documentation Archive

三.线程生命周期

Xnip2021-08-07_21-11-06.jpg 线程池 Xnip2021-08-07_21-17-06.jpg

饱和策略 • AbortPolicy 直接抛出RejectedExecutionExeception异常来阻⽌系统正常运⾏

• CallerRunsPolicy 将任务回退到调⽤者

• DisOldestPolicy 丢掉等待最久的任务

• DisCardPolicy 直接丢弃任务 

这四种拒绝策略均实现的RejectedExecutionHandler接⼝

四.多线程面试题

任务的执行速度的影响因素

  1. cpu调度
  2. 任务的复杂度
  3. 优先级
  4. 线程状态

优先级翻转 (IO vs CPU 优先级提升)

  1. IO密集型 频繁等待
  2. CPU密集型 很少等待
  3. 饿死
  4. 调度

优先级因素

  1. 用户指定
  2. 等待的频繁度
  3. 长时间不执行

五.自旋锁和互斥锁

  • 互斥锁:发现其他线程执行 当前线程 休眠 (就绪状态) 一直在等打开 唤醒执行

  • 自旋锁:发现其他线程执行 当前线程 询问 - 忙等 耗费性能比较高

  • atomic  是原子属性,是为多线程开发准备的,是默认属性!仅仅在属性的 setter 方法中,增加了锁(自旋锁),能够保证同一时间,只有一条线程对属性进行操作同一时间 单(线程)写多(线程)读的线程处理技术

  • nonatomic  是非原子属性 没有锁!性能高! objc 源码可以找到设置属性的时候对应atomic的加锁操作 保证多线程的读写操作 Xnip2021-08-07_21-42-55.jpg

Xnip2021-08-07_21-43-05.jpg

Xnip2021-08-07_21-43-43.jpg

Xnip2021-08-07_21-46-39.jpg

六.GCD初探函数和队列

  • GCD概念 将任务添加到队列,并且指定执⾏任务的函数
  • 串行队列 数据结构FIFC 先进先出 调度一个
  • 并发队列 先调度过来不一定先执行 调度多个

串行同步死锁

`dispatch_queue_t queue = dispatch_queue_create("test", NULL);

    NSLog(@"1");

    dispatch_async(queue, ^{

        NSLog(@"2");

        dispatch_sync(queue, ^{

            NSLog(@"3");

        });

    });

    NSLog(@"5");`

_dispatch_sync_f_slow 死锁