进程和线程是计算机的基础概念,是算法岗开发岗面试必问问题。下面我们就来看一下,以下的知识你是否全部了解吧
目录:
- 进程、线程、协程的概念
- 进程和线程的区别
- 协程和线程的区别
- 何时使用多进程,何时使用多线程?
- 为什么会有线程?
- *python多线程存在的问题
- *进程的几种通信方式
- *举例说明进程、线程、协程
一、进程、线程、协程的概念
进程:
是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。
线程:
是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。
协程:
是一种比线程更加轻量级的存在。一个线程也可以拥有多个协程。其执行过程更类似于子例程,或者说不带返回值的函数调用。
二、进程和线程的区别
地址空间:
线程共享本进程的地址空间,而进程之间是独立的地址空间。
资源:
线程共享本进程的资源如内存、I/O、cpu等,不利于资源的管理和保护,而进程之间的资源是独立的,能很好的进行资源管理和保护。
健壮性:
多进程要比多线程健壮,一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。
执行过程:
每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口,执行开销大。
但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,执行开销小。
可并发性:
两者均可并发执行。
切换时:
进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程。
其他:
线程是处理器调度的基本单位,但是进程不是。
三、协程和线程的区别
协程避免了无意义的调度,由此可以提高性能,但程序员必须自己承担调度的责任。同时,协程也失去了标准线程使用多CPU的能力。
线程
相对独立
有自己的上下文
切换受系统控制;
协程
相对独立
有自己的上下文
切换由自己控制,由当前协程切换到其他协程由当前协程来控制。
四、何时使用多进程,何时使用多线程?
对资源的管理和保护要求高,不限制开销和效率时,使用多进程。
要求效率高,频繁切换时,资源的保护管理要求不是很高时,使用多线程。
五、为什么会有线程?
每个进程都有自己的地址空间,即进程空间,在网络或多用户换机下,一个服务器通常需要接收大量不确定数量用户的并发请求,为每一个请求都创建一个进程显然行不通(系统开销大响应用户请求效率低),因此操作系统中线程概念被引进。
六、*python多线程的问题(面试问题)
存在问题:
python由于历史遗留的问题,严格说多个线程并不会同时执行(没法有效利用多核处理器,python的并发只是在交替执行不同的代码)。
多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。所以python的多线程并发并不能充分利用多核,并发没有java的并发严格。
原因:
【操作系统|面试必考 | 进程和线程的区别】原因就在于GIL ,在Cpython 解释器(Python语言的主流解释器)中,有一把全局解释锁(GIL, Global Interpreter Lock),在解释器解释执行Python 代码时,任何Python线程执行前,都先要得到这把GIL锁。
这个GIL全局锁实际上把所有线程的执行代码都给上了锁。
这意味着,python在任何时候,只可能有一个线程在执行代码。
其它线程要想获得CPU执行代码指令,就必须先获得这把锁,如果锁被其它线程占用了,那么该线程就只能等待,直到占有该锁的线程释放锁才有执行代码指令的可能。
多个线程一起执行反而更加慢的原因:
同一时刻,只有一个线程在运行,其它线程只能等待,即使是多核CPU,也没办法让多个线程「并行」地同时执行代码,只能是交替执行,因为多线程涉及到上线文切换、锁机制处理(获取锁,释放锁等),所以,多线程执行不快反慢。
什么时候GIL 被释放?
当一个线程遇到I/O 任务时,将释放GIL。
计算密集型(CPU-bound)线程执行100次解释器的计步(ticks)时(计步可粗略看作Python 虚拟机的指令),也会释放GIL。
即,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。
Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。
本条参考博客:
http://www.sohu.com/a/230407177_99992472
七、*进程通信方式(选读)
管道:
速度慢,容量有限,只有父子进程能通讯
FIFO:
任何进程间都能通讯,但速度慢
消息队列:
容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
信号量:
不能传递复杂消息,只能用来同步
共享内存区:
能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存
本条参考博客:
https://blog.csdn.net/weixin_40283480/article/details/82155704
八、*举例说明进程、线程、协程
程序:
例如main.py这是程序,是一个静态的程序。
python进程:
一个程序运行起来后,代码+用到的资源 称之为进程,它是操作系统分配资源的基本单元。
multiprocessing.Process实现多进程
进程池:
如果要启动大量的子进程,可以用进程池的方式批量创建子进程。
multiprocessing.Pool
进程间通信:
各自在独立的地址空间,并不能直接进行全局的数据共享,在创建子进程的时候会将父进程的数据复制到子进程中一份。
进程间通信 Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。
python线程:
thread是比较低级,底层的模块,threading是高级模块,对thread进行了封装,可以更加方便的被使用。
python协程:
线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员,当程序中存在大量不需要CPU的操作时(例如 I/O),适用于协程。
例如yield
其中 yield 是python当中的语法。
当协程执行到yield关键字时,会暂停在那一行,等到主线程调用send方法发送了数据,协程才会接到数据继续执行。
但是,yield让协程暂停,和线程的阻塞是有本质区别的。
协程的暂停完全由程序控制,线程的阻塞状态是由操作系统内核来进行切换。
因此,协程的开销远远小于线程的开销。
最重要的是,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。
这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。
python可以通过 yield/send 的方式实现协程。在python 3.5以后,async/await 成为了更好的替代方案。
推荐阅读
- Java核心知识|面试必问系列 --- 多线程安全问题
- 多线程|wait和notify实现线程之间的通信
- 多线程|Java多线程基础(线程与进程的区别,线程的创建方式及常用api,线程的状态)
- 多线程|单例模式中的线程安全问题
- 面试|如何保证数据库和缓存双写一致性
- 数据结构|每日一学丨Redis 面霸篇(从高频问题透视核心原理)
- java工程师面试篇|进阿里了——分享一波进大厂经验
- 面试|四年Java 欢聚时代面经,已拿offer
- java|Offer收割机来了,肝了这份Java面试八股文,斩获17张大厂Offer