当前位置:嗨网首页>书籍在线阅读

04-并发方法

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

8.2.1 并发方法

我们使用并发拆解程序的一部分以独立运行。有时,这些部分可能彼此依赖,并朝着共同的目标前进,或者它们可能是令人尴尬的并行,这个术语用于形容可能分解成独立的无状态任务的问题,例如,以并行的方式转换图像的每个像素。因此,采用的让程序实现并发的方法取决于我们利用并发的程度,以及我们试图解决的问题的性质。在本小节中,我们将讨论可用的并发方法。

以内核为基础

当前多任务处理已经非常普遍,如操作系统需要处理多个进程。因此,操作系统内核已经为用户提供了以下形式之一的原语来编写并发程序。

  • Processes:在这种方法中,我们可以通过生成自己的独立副本来运行程序的不同部分。在Linux上,这可以通过调用fork系统来实现。要向生成的进程传递任何数据,可以使用各种进程间通信(Inter Process Communication,IPC)工具,例如管道和FIFO。

基于并发的进程为你提供了诸如故障隔离之类的功能,但也存在启动整个新进程的开销。在操作系统内存耗尽并终止进程之前,可以生成有限数量的进程。

在Python的多进程模块中可以看到基于进程的并发。

  • Threads:底层的进程只是线程,具体来说就是主线程。进程可以启动或生成一个甚至多个线程。线程是最小的可调度执行单元。每个进程都是以一个主线程开始的。除此之外,它还可以使用操作系统提供的API生成其他线程。为了允许程序员使用线程,大多数程序语言都在其标准库中附带了线程API。与进程相比,它们是轻量级的。线程与其父进程共享相同的地址空间,它们不需要在内核中的进程控制块拥有单独的条目,每次生成新进程时都会自动更新。在一个进程中管理其中的多个线程是一项挑战,因为与进程不同,它们与父进程和其他子线程共享地址空间,并且由于线程的调度由操作系统决定,我们不能依赖线程执行的顺序,以及它们从中读取或写入内存的顺序。当我们从单线程程序转到多线程程序时,这些操作会突然变得难以理解。

127.png 注意

线程和进程的实现在不同的操作系统之间有所不同。在Linux下,内核对它们的处理相同,不同之处在于线程在内核中没有自己的进程控制块条目,并且它们与其父进程和任何其他子线程共享地址空间。

用户级别

基于进程和线程的并发性受限于我们可以生成多少个进程或线程。更轻量级和更有效的替代方案是使用用户空间线程,通常也称绿色线程。它们最先出现在Java中,代码名称包含“green”,因此该名称就沿用到现在。诸如Go(goroutine)和Erlang也有绿色线程。使用绿色线程主要是为了减少基于进程和线程的并发带来的开销。绿色线程非常轻量级,生成新线程的开销和空间都比普通线程少。例如,在Go中,goroutine只需要4KB的空间,而普通的线程需要8MB的空间。

用户空间线程会作为语言运行的一部分进行管理和调度。运行时是在运行每个程序时执行的任何额外启动或管理代码。这将是你的GC或线程调度器。在内部,用户空间线程是在原生的操作系统线程上实现的。Rust在其1.0版本之前有绿色线程,但是在Rust发布稳定版之前它们就被移除了。包含绿色线程将违反Rust的“承诺”和运行时零成本原则。

用户空间的并发更高效,但是正确地付诸实践却难度很大。不过基于线程的并发是一种经过检验和测试的方法,并且由于多进程操作系统的出现而广受欢迎,因此它是实现并发的首选方法。大多数主流语言都提供了线程API,允许用户创建线程,并方便地拆卸其余无关的代码以独立执行。

在程序中利用并发需要遵循多个步骤。首先,我们需要识别出任务中能够独立运行的部分。其次,我们需要找到协调线程的方法,这些线程被拆分为多个子任务一起实现共同的目标。在此过程中,线程可能还需要共享数据,并且需要同步才能访问或写入共享数据。在享受并发带来的所有好处的同时,开发者需要关注和面对一系列新的挑战和范式。在8.2.2小节中,我们将讨论并发的缺陷。