购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.3 线程和进程

CPU、GPU和网卡等概念属于硬件层面。在软件层面,线程(Thread)或进程(Process)常用于描述程序的运行过程。

1.3.1 进程与线程

CPU、GPU和网卡等硬件设备都由操作系统(Operating System,OS)管理。操作系统不仅管理硬件资源,还通过各类应用软件为用户提供服务。正在运行的软件称为进程(Process)。以个人笔记本电脑为例,当使用浏览器浏览网页时,操作系统会创建一个浏览器进程;使用文本编辑软件Word编写文字内容时,操作系统会创建一个Word进程。macOS上的活动监视器(见图1.4)和Windows上的任务管理器都能够显示操作系统当前运行的进程,以及各个进程对CPU、内存等资源的占用情况。

在技术层面上,操作系统管理所有进程的执行,给进程合理地分配资源:操作系统以进程为单位分配主存空间,每个进程都有自己独立的主存地址空间、数据栈等。

在大部分编程语言的实现中,一个进程通常包含多个线程,如图1.5所示。每个线程可以在一个物理计算核心上运行,一个进程的多个线程可利用多个物理计算核心并行执行。

图1.4 macOS的活动监视器

图1.5 进程和线程

从图1.5可以看出,一个进程可以包含多个并发执行的线程。多个线程共享相同的上下文(数据、文件等),因此线程之间的数据共享和通信相对容易。

进程之间是相互隔离的,如果多个进程之间需要交换数据,必须通过进程间通信机制(Inter-Process Communication,IPC)来实现数据共享。例如,可以使用多个进程共享内存(multiprocessing.shared_memory),或者使用消息传递接口(Message Passing Interface,MPI),后者将在11.1节中介绍。

1.3.2 线程安全

由于多个线程共享上下文(数据、文件等),当不同线程访问同一数据时,容易引发线程安全(Thread Safe)问题。例如:

    x = x + 1
    x = x * 2
    x = x - 1

如果这三行计算被调度到三个线程上,数据 x 将由这三个线程共享,且它们的执行顺序会显著影响计算结果。图1.6展示了三种不同的执行时序,这三种执行时序会导致不同的计算结果。由于调度时序影响,可能出现并行计算的结果不符合预期的情况,导致线程不安全。

图1.6 线程安全

解决线程安全问题最简单的办法是加锁。如图1.7所示,每个线程在修改共享数据之前先加锁,完成修改后再释放锁。

图1.7 线程锁

1.3.3 全局解释器锁

Python解释器CPython在解决线程安全问题时采用了一个激进的方式:全局解释器锁(Global Interpreter Lock,GIL)。

提示

CPython是Python解释器的一种实现,也是使用最广泛的一种实现。CPython主要使用C语言实现。我们通过Anaconda安装的Python就是CPython。

除了CPython,还有Jython(使用Java实现)和IronPython(使用C#实现)。

GIL(全局解释器锁)使得Python在一个进程下只允许一个线程运行。在GIL的限制下,一个Python进程只能利用一个CPU核心。GIL的优势之一是能够确保线程安全,但它的缺点也很明显:在现代的单机多核计算机上,Python及其大部分库(如NumPy、Pandas、Scikit-learn)的功能只能使用一个CPU核心。

GIL与整个Python生态紧密相关,它影响了NumPy、Pandas等许多库。移除GIL可谓牵一发而动全身。因此,要让Python充分利用现代计算机的多核计算能力,其实并不容易。 LgOcLl81+5Mnr/93Gpy49I6UNgIP1vkIQPjYEe2NwNYfIdo4MqxfdRHdL61mwU1F

点击中间区域
呼出菜单
上一章
目录
下一章
×