线程和进程的关系和区别
线程定义
- 线程是进程的基本执行单元,一个进程的所有任务都在线程中执行
- 进程要想执行任务,必须得有线程,进程至少要有一条线程
- 程序启动会默认开启一条线程,这条线程被称为主线程或 UI 线程
进程定义
- 进程是指在系统中正在运行的一个应用程序
- 每个进程之间是独立的,每个进程均运行在其专用的且受保护的内存空间内
- 通过“活动监视器”可以查看 Mac 系统中所开启的进程
进程与线程的区别
- 地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
- 资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的。
- 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
- 进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程
- 执行过程:每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
- 线程是处理器调度的基本单位,但是进程不是。
多线程的意义
优点
- 能适当提高程序的执行效率
- 能适当提高资源的利用率(CPU,内存)
- 线程上的任务执行完成后,线程会自动销毁
缺点
- 开启线程需要占用一定的内存空间(默认情况下,每一个线程都占 512 KB)
- 如果开启大量的线程,会占用大量的内存空间,降低程序的性能
- 线程越多,CPU 在调用线程上的开销就越大
-
程序设计更加复杂,比如线程间的通信、多线程的数据共享
好像主线程也是512KB,而不是1MB
多线程的原理
多线程的生命周期
- 新建线程,执行start,线程必须start,否则没有作用。但也不可以重复start,会导致崩溃
- 线程start后,将会进入Runnable状态。
- 进入Runnable状态后,进入Running状态,当前线程会被CPU调度。也有可能在进入Running状态后,线程切换,返回Runnable状态
CPU如何调度线程?线程池的原理
1.系统有一个可调度线程池,如果线程池里有这个线程,就会开始执行。
2.如果没有,将判断线程池大小是否小于核心线程池大小,如果小于,那将会开辟新的线程来执行任务。
3.如果大于线程池,代表线程池没有能力再去开辟新的线程,只能依赖于原有的线程去执行 ,就将判断工作队列是否已满,如果没满就将任务提交到工作队列,等待执行任务。
4.满了的话,线程池就会判断所有线程是否都在工作,如果有空闲的线程,就让这条线程去执行任务。
5.如果还是没有空闲的线程,就将任务交给饱和策略。
6.饱和策略分为四种Abort策略:默认策略,新任务提交时直接抛出未检查的异常RejectedExecutionException,该异常可由调用者捕获。
CallerRuns策略:为调节机制,既不抛弃任务也不抛出异常,而是将某些任务回退到调用者。不会在线程池的线程中执行新的任务,而是在调用exector的线程中运行新的任务。
Discard策略:新提交的任务被抛弃。
DiscardOldest策略:队列的是“队头”的任务,然后尝试提交新的任务。(不适合工作队列为优先队列场景)
- Running后,有可能任务会造成堵塞,当造成堵塞的任务执行结束后,同样会回到Runnable状态,继续执行Running
- 死亡,分为任务执行结束跟强制退出线程exit
线程操作
查看NSThread相关文件可以看到,线程有多种状态,可以根据不同情况,判断线程的状态,来做不同的操作。可通过重写下面的方法掌握线程的生命周期(YYKit)。
一般情况下,我们很少去中止一个网络请求,按照一般的情况,是没办法中止的,但是依靠线程,通过关闭线程,从而达到中止网络请求的目的。
@property (readonly, getter=isExecuting) BOOL executing API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@property (readonly, getter=isFinished) BOOL finished API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@property (readonly, getter=isCancelled) BOOL cancelled API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (void)cancel API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (void)start API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (void)main API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); // thread body method
线程安全
这是一张经典的多线程比喻图。多端增删改查。
如何避免上图的情况?如何保证线程安全?
上锁