进程和线程通俗讲(进程和线程的主要区别(总结))

并发(Concurrency)是多个任务或多个计算在同一时间段内发生的能力,并发的目的是允许系统更有效地使用资源,或在等待某些任务(例如,I/O操作)完成时执行其他任务。

例如,在 GUI 应用程序中,用户界面线程可以响应用户输入,而另一个线程在后台进行计算或 I/O 操作,这样用户界面不会因为后台任务的等待而冻结。

我们来阐述三个基本构造并发程序的方法:

进程
每个逻辑控制流都可以看作是一个进程,由操作系统的内核来进行调度和管理,因为每个进程有独立的虚拟地址空间,它们之间想要通信,需要显示的使用 IPC 通信机制。
考虑这样一个场景:假设我们有两个客户端和一台服务器,服务器正在监听 3 号端口上的请求,假设现在服务器接受了客户端 1 的请求,并返回一个已连接描述符。

在接受连接请求之后,服务器生成一个子进程,这个子进程获得服务器描述符的完整副本,子进程关闭它副本中监听的 3 号描述符,同时父进程关闭其已连接的 4 号描述符,接着,子进程为客户端 1 提供服务。

进程和线程通俗讲(进程和线程的主要区别(总结))

因为父子进程都是指向的同一个文件表表项,所以父进程关闭它的已连接描述符的副本是很重要的,否则就永远不会释放已连接的 4 号描述符,而且由此引起的内存泄露最终消耗完所有可用内存,使系统崩溃。

之后,父进程接受一个新的客户端 2 的连接请求,并返回一个新的连接 5 号描述符。

进程和线程通俗讲(进程和线程的主要区别(总结))

再次,父进程又派生另一个子进程,这个子进程用已连接的 5 号描述符来为它的客户提供服务。

此时,父进程还在继续等待下一个连接请求,而两个子进程并发地为它们各自的客户端提供服务。
对于父子进程间共享状态信息,有一个非常清晰的模型:共享文件描述符表,但不共享用户地址空间,由于每个进程都有独立的地址空间,每个进程的虚拟内存都是单独维护的,所以不存在相互覆盖的问题。
进程间若要共享信息,必须采用 IPC 通信机制,但这种基于进程来设计的方式往往相对比较慢,因为进程控制和 IPC 通信的开销很高。
I/O 多路复用
在这种并发编程模式中,应用程序在单个进程的上下文中显式地调度自己的逻辑控制流。
假设应用要处理两个 I/O 交互,一个是客户端发起的网络请求,一个是在键盘上输入的命令行。我们先执行哪个事件呢?
传统的 I/O 操作是阻塞性的,如果我们执行命令行,那网络连接请求就需要等待;如果我们处理网络连接请求,那命令行就需要等待。
I/O 多路复用提供了解决方案,使程序能够在一个进程内并发地监听多个 I/O 通道:
  • 首先,创建一个 I/O 多路复用实例;
  • 将要监听的通道(套接字、命令行输入)注册到该实例上。
  • 程序进入一个等待的状态,等待已经注册好的通道,并且通道准备好「读或写」操作;
  • 当某个或多个 I/O 通道准备好时,多路复用系统将返回这些活跃的通道;
  • 应用程序立即处理这些活跃的通道,而不必等待其他未准备好的通道;

一个单独的进程可以创建多个并发的 I/O 事件,避免创建多个进程的开销。

I/O 多路复用解决的是准备好的通道,而不是处理通道阻塞的问题(多个通道同时响应),而实际的处理(如读/写操作)仍然是顺序执行的。

线程
在之前的方法中,我们讨论了利用「进程」来处理并发,其中每个控制流都是独立的进程,由内核调度。因为每个进程都有独立的地址空间,数据共享变得复杂,尽管有 IPC 通信,但进程间的通信开销仍然较大。
我们还探讨了 I/O 多路复用技术,它允许在单一进程中显式调度多个逻辑流,这使得数据共享变得简单,但多通道并发响应时的处理仍然是串行的,因此并发效果有限。
下面我们将介绍基于「线程」的方法,它是这以上两种方法的融合。
线程是在单一进程上下文中执行的逻辑控制流,由内核负责调度,每个线程都有自己的执行上下文,这包括线程ID、栈、栈指针、程序计数器、通用寄存器和条件码,所有属于同一个进程的线程共享该进程的全部虚拟地址空间。

每个进程启动时只有一个线程,即主线程。在其生命周期的某个时刻,主线程可以创建多个对等线程,它们并发运行,可以读写同一块共享数据。
主线程主要负责监听新连接和现有连接上的 I/O 活动。
启动时,主线程初始化一个线程池,其中所有线程初始状态均为闲置,等待主线程分配任务。
利用 I/O 多路复用,主线程可以监听多个通道「套接字、命令行」。当出现新的连接请求或现有连接就绪时,多路复用通知主线程。
对于新的连接请求,主线程自行处理,并加入到多路复用的监听集合中。一旦线程池中的工作线程被分配任务,它就开始执行相应的 I/O 操作和数据处理。
分配任务后,主线程不会等待该任务完成,而是回到多路复用继续监听其他 I/O 活动。
线程提供了一种高效的并发编程方法,与传统进程相比,它有以下优点:
  • 线程的上下文切换要比进程小得多,快得多;
  • 主线程可以快速响应事件,而不需要等待完成;
  • 多个工作线程可以并行的处理多个 I/O 任务;
  • 使用线程池避免了频繁的创建和销毁线程的开销;
线程为并发编程提供了一种高效、灵活且经济的方法,使得开发者可以更好地管理和调度系统资源,从而满足高并发、高响应的应用需求。

内容优化:ChatGPT
内容来源:《深入理解计算机系统》

原创文章,作者:小道研究,如若转载,请注明出处:https://www.sudun.com/ask/34607.html

(0)
小道研究's avatar小道研究
上一篇 2024年4月9日 下午8:15
下一篇 2024年4月9日 下午8:17

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注