知识的学习在于点滴记录,坚持不懈;知识的学习要有深度和广度,不能只流于表面,坐井观天;知识要善于总结,不仅能够理解,更知道如何表达!
进程线程是一个很容易被问到的面试题,在面对这种题时我们应该怎么答呢?
进程时资源分配的基本单位,线程是资源调度的基本单位?
漏!大漏特漏!
我们应该试着从这些方面去答:
- 单线程、多线程(又分内核级线程和用户级线程)
- 单进程、多进程(TLB失效)
- 对于互斥资源我们还要进行 线程/进程 间互斥操作
- 线程/进程 间的同步操作
我们下面讨论就不设计多核讨论了
单进程/单线程
个人认为,单线程也可以理解成单进程去认识;
当一个进程启动运行时,操作系统会将CPU的控制权给到进程,同时分配内存、寄存器等系统资源,既然可以当做单线程,那也可以说单线程享有这些资源;
- 那这存在什么问题呢?
答:我们在一个程序中通常会有大量的IO操作,比如访问磁盘、接受网络对端发过来的数据,在IO就绪之前,进程会处于阻塞态无法进行,进程就会让出CPU,直到IO就绪,才会唤醒CPU继续之前的操作。IO通常是比较慢的,这就造成CPU的性能不能被很好地利用;
以上,我们引入了多进程
多进程
单核情况下,由于有多个进程都需要执行,我们以AB两个进程举例子,当A进程在执行操作时,遇到IO事件,让出CPU,接着B进程拿到CPU的控制权就会执行,当A的IO事件就绪后会唤醒进程,A再拿到CPU控制权进行后续操作;
这样一来多进程就使得CPU多运行少阻塞以提高性能,换言之依旧是通过多进程的并发提高性能;
那多进程就完美了吗?
我对于这个问题,我们需要从切换进程发生什么这个角度去分析;
多进程中每个进程就好比每一门功课,并发执行中,当我们写语文作业时,写一会儿要去写数学,就会清理战场;收起语文课本,装起来钢笔(保存状态),拿出数学课本、三角尺、铅笔(设置新的运行环境)这样的操作;
从上面我们可以看到进程的切换还是有一定的开销的,然后我们引入多线程解决这一问题;
多线程
我们仍以单核的情况进行讨论;
一个进程的内部被划分为多个线程,其中主线程的生命周期是和进程共进退的;
多进程中切换进程时产生的进程间切换产生的开销,在多线程中就得到了改善;
进程中有多个线程(比如1、2线程),线程1在执行中碰到IO事件后,线程调度器就会使得线程2介入,这其中的开销就比进程切换小了;
进程是资源分配的基本单位,它拥有一个完整的虚拟地址空间
,多线程共享这里面的资源,不过操作系统为每个线程还维护了一个独立的栈,线程间独立栈是不可被交叉访问的;
线程间的切换只需要保存少量的寄存器(如程序计数器)就可以做到上下文切换只产生少量开销;
最终要的是,因为进程间不共享地址空间,进程切换操作会带来TLB(一个高速cache)失效的问题!统一进程的线程共享进程的虚拟地址空间,线程间的切换不会使得TLB失效!
总结一下:
- 线程间切换只需要保存少量寄存器,且线程各自维护一个独立栈空间
- 线程间切换不会使得TLB失效
通信
进程间
- pipe无名管道(半双工)
- fifo命名管道(全双工)
- 消息队列
- 共享内存
- socket
这里引入帅地前辈的一篇进程间通信,一步一步让你记住这些
进程间的互斥操作可以用互斥锁、信号量解决
线程间
由于同一进程内多线程是共享进程的虚拟地址空间的,那么他们的通信就很好解决了,直接访问共享空间就好
线程间的互斥操作可以使用互斥锁+条件变量解决;甚至可以不用信号量;
以上我相信大家对线程进程的感受就比较深了,下次再遇到这样的问题就不用两句话结束,下面我们再稍微总结一下;
- 进程是资源分配的基本单位,线程是资源调度的基本单位
- 避免IO操作的阻塞,可以进程多进程的并发执行
- 避免进程间开销过大,引入线程的并发执行
- 每个进程享有独立的虚拟地址空间,进程切换TLB失效
- 多线程维护独立的栈空间,共享进程的虚拟地址空间,线程切换TLB不失效
下面聊一个面试可能被问到的问题,并给出我个人理解
- 计算密集型和IO密集型怎么选择多进程和多线程?
答:
-
计算密集型是需要CPU进行大量运算,通常需要获得更长的CPU时间片而不是频繁切换,所以使用多进程;
-
IO密集型不需要CPU提供强大算力,可以通过频繁切换达到高并发效果,因此使用多线程
如果这篇文章有帮助到你希望留下一个赞,文中如果有不对或者不理解的地方也欢迎指出讨论交流!