“这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战”
一.线程和进程
线程是进程的基本执⾏单元,⼀个进程的所有任务都在线程中执⾏
- 进程想要执行任务,必须得有线程,进程至少要有一条线程
- 程序启动默认开启一条线程,这条线程被称为主线程或UI线程 进程是指在系统中正在运行的一个应用程序
- 每个进程之间是独立的,每个进程均运行在其专用的且受保护的内存空间内
- 通过“活动监视器”可以查看Mac系统中所开启的进程
mac有多进程 iOS没有多进程 有图标显示代表有系统桌面权限的
进程与线程的关系
地址空间:同⼀进程的线程共享本进程的地址空间,⽽进程之间则是独⽴的地址空间。
资源拥有:同⼀进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的
资源是独⽴的。
-
⼀个进程崩溃后,在保护模式下不会对其他进程产⽣影响,但是⼀个线程崩溃整个进程都死掉。所以多进程要⽐多线程健壮。
-
进程切换时,消耗的资源⼤,效率⾼。所以涉及到频繁的切换时,使⽤线程要好于进程。同样如果要求同时进⾏并且⼜要共享某些变量的并发操作,只能⽤线程不能⽤进程
-
执⾏过程:每个独⽴的进程有⼀个程序运⾏的⼊⼝、顺序执⾏序列和程序⼊⼝。但是
-
线程不能独⽴执⾏,必须依存在应⽤程序中,由应⽤程序提供多个线程执⾏控制。
-
线程是处理器调度的基本单位,但是进程不是。
-
线程没有地址空间,线程包含在进程地址空间中
TLS:存储 例如
二.多线程原理
优点
- 能适当提高程序的执行效率
- 能适当提高资源的利用率
- 线程上的任务执行完成后,线程会自动销毁 缺点
- 开启线程需要占用一定的内存空间(默认情况下,每一个线程都占512KB)
- 如果开启大量的线程,会占用大量的内存空间,降低程序的性能
- 线程越多,CPU在调用线程上的开销就越大
- 程序设计更加复杂,比如线程间的通信,多线程的数据共享
时间⽚的概念:CPU在多个任务直接进⾏快速的切换,这个时间间隔就是时间⽚ (单核CPU)同一时间,CPU只能处理1个线程 换言之,同一个时间只有1个线程在执行 多线程同时执行: 是CPU快速的在多线程之间的切换 CPU调度线程的时间足够快,就造成了多线程的“同时”执行的效果 如果线程数非常多 CPU会在N个线程之间切换,消耗大量的CPU资源 每个线程被调度的次数会降低,线程的执行效率降低
三.线程生命周期
线程池
饱和策略
• AbortPolicy 直接抛出RejectedExecutionExeception异常来阻⽌系统正常运⾏
• CallerRunsPolicy 将任务回退到调⽤者
• DisOldestPolicy 丢掉等待最久的任务
• DisCardPolicy 直接丢弃任务
这四种拒绝策略均实现的RejectedExecutionHandler接⼝
四.多线程面试题
任务的执行速度的影响因素
- cpu调度
- 任务的复杂度
- 优先级
- 线程状态
优先级翻转 (IO vs CPU 优先级提升)
- IO密集型 频繁等待
- CPU密集型 很少等待
- 饿死
- 调度
优先级因素
- 用户指定
- 等待的频繁度
- 长时间不执行
五.自旋锁和互斥锁
-
互斥锁:发现其他线程执行 当前线程 休眠 (就绪状态) 一直在等打开 唤醒执行
-
自旋锁:发现其他线程执行 当前线程 询问 - 忙等 耗费性能比较高
-
atomic是原子属性,是为多线程开发准备的,是默认属性!仅仅在属性的setter方法中,增加了锁(自旋锁),能够保证同一时间,只有一条线程对属性进行写操作同一时间 单(线程)写多(线程)读的线程处理技术 -
nonatomic是非原子属性 没有锁!性能高!objc源码可以找到设置属性的时候对应atomic的加锁操作 保证多线程的读写操作
六.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 死锁