多线程和多进程的区别
Linux内核实际上是不区分进程和线程的,它们都是用的task_struct结构体,Linux上的线程是LWP,即轻量级进程。创建进程的fork()函数和创建线程的clone()函数内核底层都调用了do_fork()函数。对于线程来说,使用了CLONE_VM(共享内存空间)、CLONE_FS(共享文件系统信息)、CLONE_FILES(共享文件描述符表)、CLONE_SIGHAND(共享信号处理函数)等属性,所以线程是共享地址空间的。而创建进程调用do_fork()则不会使用任何共享属性,进程拥有独立的运行环境。不过由于fork()采用写时拷贝,所以创建进程开销也有一定的优化。
进程切换也比线程开销大。因为进程切换时,不仅要保存相关寄存器和栈的信息,而且要更新CR3寄存器,也就是切换页表,刷新TLB,而且往往伴随着页面调度,因为进程的数据段代码段都要换出去,以便将要执行的进程内容换进来。而线程由于共享地址空间,切换是只需保存线程的上下文(即相关寄存器和栈的信息)就好了。
同样由于共享地址空间,同一进程的不同线程之间很容易共享数据,而进程则需要IPC机制来通信。不过,由于不共享地址空间,进程容错性更好一些,一个进程挂掉不会导致系统崩溃。多线程在多核架构下容易发生伪共享现象(false sharing),由于数据共享,一个CPU修改了数据,其余CPU的cache全部失效并重新加载。而多进程共享的内存是程序员控制的,一般不会出现伪共享。
多线程适合与SMP架构,多进程还可以扩展到多台机器。
如下表:
对比维度 | 多进程 | 多线程 | 总结 |
---|---|---|---|
数据同步、共享 | 数据共享复杂,需要用IPC;数据是分开的,同步简单 | 因为共享进程数据,数据共享简单,但也是因为这个原因导致同步复杂 | 各有优势 |
内存CPU | 内存占用多,切换复杂,CPU利用率低 | 占用内存少,切换简单,CPU利用率高 | 线程占优 |
创建销毁、切换 | 创建销毁、切换复杂,速度慢 | 创建销毁、切换简单,速度很快 | 线程占优 |
编程、调试 | 编程简单,调试简单 | 编程复杂,调试复杂 | 进程占优 |
可靠性 | 进程间不会相互影响 | 一个线程挂掉导致整个进程挂掉 | 进程占优 |
分布式 | 适用于多核、多机分布式;可扩展多台机器 | 适用于多核分布式 |
如何选择?
- 需要频繁创建销毁优先用线程
- 需要进程大量计算优先用线程(耗费CPU,切换频繁)
- 强相关的处理用线程,弱相关的处理用进程(也就是数据交换多的用线程)
- 可能要扩展到多机分布的用进程,多核分布用线程
参考:
1. 有关linux下多进程与多线程的区别总结
2. 进程切换
3. 多线程伪共享(false sharing)问题分析
4. Linux 下多线程和多进程程序的优缺点,各自适合什么样的业务场景?