这是我参与更文挑战的第16天,活动详情查看: 更文挑战
单线程VS多线程
假设以下有这四个任务
- 任务 1 是计算 A=1+2;
- 任务 2 是计算 B=20/5;
- 任务 3 是计算 C=7*8;
- 任务 4 是显示最后计算的结果。
如果用单线程来处理,就是分四步按照顺序分别执行这四个任务。
如果采用多线程来处理,只需要分两步即可,第一步,使用三个线程同时执行前三个任务;第二步执行第四个任务:显示计算结果。
通过对比你会发现,单线程执行需要四步,使用多线程只需要两步,因此,使用并行处理可以大大提升性能。
线程VS进程
多线程可以并行处理任务,但是线程是不能单独存在的,它是由进程来启动和管理的,那么什么是进程呢?
一个进程就是一个程序的运行实例。详细解释就是,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程。、
加上进程的概念之后再去看单线程和多线程处理:
图中可知,线程是依附于进程的,而进程中使用多线程并行处理能提升运算效率。
进程和线程之间的关系有以下 4 个特点。
1. 进程中的任意一线程执行出错,都会导致整个进程的崩溃。
比如把之前的任务中的B任务改成B=20/0
A = 1+2
B = 20/0
C = 7*8
在计算B的值的时候,因为分母是0,线程就会执行出错,这样就会导致整个进程崩溃,当然另外两个线程执行的结果也没有了。
2.线程之间共享进程中的数据。
线程之间可以对进程的公共数据进行读写操作。
线程 1、线程 2、线程 3 分别把执行的结果写入 A、B、C 中,然后线程 2 继续从 A、B、C 中读取数据,用来显示执行结果。
3.当一个进程关闭之后,操作系统会回收进程所占用的内存。
当一个进程退出时,操作系统会回收该进程所申请的所有资源;即使其中任意线程因为操作不当导致内存泄漏,当进程退出时,这些内存也会被正确回收。
内存泄漏:内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
4.进程之间的内容相互隔离。
进程隔离是为保护操作系统中进程互不干扰的技术,每一个进程只能访问自己占有的数据,也就避免出现进程 A 写入数据到进程 B 的情况。正是因为进程之间的数据是严格隔离的,所以一个进程如果崩溃了,或者挂起了,是不会影响到其他进程的。如果进程之间需要进行数据的通信,这时候,就需要使用用于进程间通信(IPC)的机制了。
进程间通信
1、管道
匿名管道
第一个命令 | 第二个命令
第一个命令的结果是第二个命令的输入。
通信的数据是无格式的流并且大小受限,通信的方式是单向的,数据只能在一个方向上流动,如果要双向通信,需要创建两个管道,再来匿名管道是只能用于存在父子关系的进程间通信,匿名管道的生命周期随着进程创建而建立,随着进程终止而消失。
命名管道
创建一个管道
在一个终端输入mkfifo pipe和echo "Hello" > pipe,可以看到此时这个进程阻塞了
在另一个终端输入cat < pipe
总结:
无论是匿名管道还是命名管道,进程写入的数据都是缓存在内核中,另一个进程读取数据时候自然也是从内核中获取,同时通信数据都遵循先进先出原则,不支持 lseek 之类的文件定位操作。
2.消息队列
消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。消息队列实际上是保存在内核的「消息链表」,消息队列的消息体是可以用户自定义的数据类型,发送数据时,会被分成一个一个独立的消息体,当然接收数据时,也要与发送方发送的消息体的数据类型保持一致,这样才能保证读取的数据是正确的。消息队列通信的速度不是最及时的,毕竟每次数据的写入和读取都需要经过用户态与内核态之间的拷贝过程。
3.共享内存
可以解决消息队列通信中用户态与内核态之间数据拷贝过程带来的开销,它直接分配一个共享空间,每个进程都可以直接访问,就像访问进程自己的空间一样快捷方便,不需要陷入内核态或者系统调用,大大提高了通信的速度,享有最快的进程间通信方式之名。但是便捷高效的共享内存通信,带来新的问题,多进程竞争同个共享资源会造成数据的错乱。
4.信号量
那么,就需要信号量来保护共享资源,以确保任何时刻只能有一个进程访问共享资源,这种方式就是互斥访问。信号量不仅可以实现访问的互斥性,还可以实现进程间的同步,信号量其实是一个计数器,表示的是资源个数,其值可以通过两个原子操作来控制,分别是 P 操作和 V 操作。
信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
5.信号
与信号量名字很相似的叫信号,它俩名字虽然相似,但功能一点儿都不一样。用于通知接收进程某个事件已经发生。信号是进程间通信机制中唯一的异步通信机制,信号可以在应用进程和内核之间直接交互,内核也可以利用信号来通知用户空间的进程发生了哪些系统事件,信号事件的来源主要有硬件来源(如键盘 Cltr+C )和软件来源(如 kill 命令),一旦有信号发生,进程有三种方式响应信号 1. 执行默认操作、2. 捕捉信号、3. 忽略信号。有两个信号是应用进程无法捕捉和忽略的,即 SIGKILL 和 SEGSTOP,这是为了方便我们能在任何时候结束或停止某个进程。
6.Socket 通信
前面说到的通信机制,都是工作于同一台主机,如果要与不同主机的进程间通信,那么就需要 Socket 通信了。Socket 实际上不仅用于不同的主机进程间通信,还可以用于本地主机进程间通信,可根据创建 Socket 的类型不同,分为三种常见的通信方式,一个是基于 TCP 协议的通信方式,一个是基于 UDP 协议的通信方式,一个是本地进程间通信方式。
以上,就是进程间通信的主要机制了。
参考: