进程是什么
进程是指在系统中正在运行的一个应用程序(App);每个进程之间是独立的,每个进程均运行在其专用的且受保护的内存空间中;通过活动监视器可以查看Mac系统中所开启的进程。
线程是什么
线程是进程的基本执行单元,一个进程的所有任务都是在线程中执行;进程想要执行任务必读得有线程,进程至少有一条线程。程序启动后会默认开启一条线程,这条线程被称作主线程或者UI线程。
进程与线程的关系
地址空间:同⼀进程的线程共享本进程的地址空间,⽽进程之间则是独⽴的地址空间。 资源拥有:同⼀进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独⽴的。
- ⼀个进程崩溃后,在保护模式下不会对其他进程产⽣影响,但是⼀个线程崩溃整个进程都死掉。所以多进程要⽐多线程健壮。
- 进程切换时,消耗的资源⼤,效率⾼。所以涉及到频繁的切换时,使⽤线程要好于进程。同样如果要求同时进⾏并且⼜要共享某些变量的并发操作,只能⽤线程不能⽤进程
- 执⾏过程:每个独⽴的进程有⼀个程序运⾏的⼊⼝、顺序执⾏序列和程序⼊⼝。但是线程不能独⽴执⾏,必须依存在应⽤程序中,由应⽤程序提供多个线程执⾏控制。
- 线程是处理器调度的基本单位,但是进程不是。
- 线程没有地址空间,线程包含在进程地址空间中
- 线程局部存储(ThreadLocalStorage,TLS)。线程局部存储是某些操作系统为线程单独提供的私有空间,但通常只具有很有限的容量。(获取同步锁的时候用到sync_data/在
objc_init
函数中也存在)
多线程的意义
多线程并不是真正原理上的并发,只是CPU在多个任务直接进⾏快速的切换,这个时间间隔就是时间⽚。真正的并发建立在一个多核的基础上
优点:
- 能适当提高程序的执行效率
- 能适当提高资源的利用率(cpu 内存)
- 线程上的任务执行完成后,线程会自动销毁 缺点:
- 开启线程需要占用一定的内存空间(默认情况下,每一个线程都占512kb)
- 如果开启大量的线程,会占用大量的内存空间,降低程序的性能
- 线程越多,CPU在调用线程上的开销就越大
- 程序设计的更加复杂比如线程之间的通信、多线程的数据共享
单核同一时间,cpu只能处理一个线程,换言之,同一时间只有1个线程在执行 多线程同时执行其实是cpu快速的在多个线程之间的切换;cpu线程调度的时间足够快,就造成了多线程的同时执行的假象。 如果线程数量非常多的话,cpu会在N个线程之间切换,消耗大量的cpu资源,每个线程被调度的次数会降低,线程的执行效率降低。
多线程的几种方案
线程生命周期
线程调度的策略
饱和策略
- AbortPolicy 直接抛出RejectedExecutionExeception异常来阻⽌系统正常运⾏
- CallerRunsPolicy 将任务回退到调⽤者
- DisOldestPolicy 丢掉等待最久的任务
- DisCardPolicy 直接丢弃任务
任务执行速度的影响因素
- cpu
- 任务的优先级
- 任务的复杂度
- 线程当前的状态
优先级翻转
- IO密集型 :频繁等待
- CPU密集型 :很少等待 为了避免出现饿死状态,cpu调度 IO密集型会提高优先级
改变线程优先级的因素
- 用户指定优先级
- 根据等待的频繁度
- 长时间不执行的任务也会提高优先级
自旋锁和互斥锁
- 互斥锁 发现其他线程执行 当前线程 休眠 (就绪状态) 一直在等打开 唤醒执行
- 自旋锁 发现其他线程执行 当前线程 询问 - 忙等 耗费性能比较高 一般使用在短小的任务中
atomic
:是原子属性,是为多线程开发准备的,是默认属性!仅仅在属性的 setter
方法中,增加了锁(自旋锁),能够保证同一时间,只有一条线程对属性进行写
操作。但是同一个时间多个线程都可以取值
nonatomic 是非原子属性。没有锁!性能高!
看源码:
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
{
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
bool mutableCopy = (shouldCopy == MUTABLE_COPY);
reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
//....
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
}
从上面我们可以看出atomic
不是锁,只是一个加锁的标识。
线程和Runloop
- runloop与线程是⼀⼀对应的,⼀个runloop对应⼀个核⼼的线程,为什么说是核⼼的,是因为runloop是可以嵌套的,但是核⼼的只能有⼀个,他们的关系保存在⼀个全局的字典⾥。
- runloop是来管理线程的,当线程的runloop被开启后,线程会在执⾏完任务后进⼊休眠状态,有了任务就会被唤醒去执⾏任务。
- runloop在第⼀次获取时被创建,在线程结束时被销毁。
- 对于主线程来说,runloop在程序⼀启动就默认创建好了。
- 对于⼦线程来说,runloop是懒加载的,只有当我们使⽤的时候才会创建,所以在⼦线程⽤定时器要注意:确保⼦线程的runloop被创建,不然定时器不会回调。
补充
__bridge
只做类型转换,但是不修改对象(内存)管理权;
__bridge_retained
将oc对象转换为CoreFoundation
,内存管理权限交给我们,后续需要使用CFRealeas或者相关的方法来释放对象
__bridge_transfer
将CoreFoundation
转换为oc对象,同时将内存的管理权限交回给ARC