Python 并发性 - 简介
本章将介绍 Python 中并发的概念,并学习不同的线程和进程。
什么是并发?
简单来说,并发是两个或多个事件同时发生。并发是一种自然现象,因为在任何给定时间,许多事件会同时发生。
在编程方面,并发是指两个任务在执行上重叠。通过并发编程,可以提高应用程序和软件系统的性能,因为我们可以并发处理请求,而不是等待前一个请求完成。
并发的历史回顾
以下几点将简要回顾并发的历史:
从铁路概念出发
并发与铁路概念密切相关。在铁路系统中,需要以确保每列火车都能安全到达目的地的方式来处理同一铁路系统上的多列火车。
学术界的并发计算
计算机科学对并发的兴趣始于 Edsger W. Dijkstra 在 1965 年发表的研究论文。在这篇论文中,他识别并解决了互斥问题,这是并发控制的一个属性。
高级并发原语
近年来,由于引入了高级并发原语,程序员正在获得改进的并发解决方案。
编程语言改进的并发性
诸如 Google 的 Golang、Rust 和 Python 等编程语言在帮助我们获得更好的并发解决方案的领域取得了令人难以置信的进展。
什么是线程和多线程?
线程是操作系统中可以执行的最小执行单元。它本身不是一个程序,而是在程序中运行。换句话说,线程彼此不独立。每个线程与其他线程共享代码段、数据段等。它们也称为轻量级进程。
线程包含以下组件:
程序计数器,包含下一个可执行指令的地址
堆栈
寄存器集
一个唯一 ID
另一方面,多线程是指 CPU 通过并发执行多个线程来管理操作系统使用的能力。多线程的主要思想是通过将一个进程分成多个线程来实现并行性。可以通过以下示例理解多线程的概念。
示例
假设我们正在运行一个特定的进程,在这个进程中,我们打开 MS Word 向其中键入内容。一个线程将被分配来打开 MS Word,另一个线程将被需要在其中键入内容。现在,如果我们想编辑现有的内容,则需要另一个线程来执行编辑任务,依此类推。
什么是进程和多进程?
进程被定义为一个实体,它表示要在系统中实现的基本工作单元。简单来说,我们用文本文件编写计算机程序,当我们执行这个程序时,它就变成了一个执行程序中所有任务的进程。在进程生命周期中,它会经历不同的阶段——启动、就绪、运行、等待和终止。
下图显示了进程的不同阶段:
一个进程可以只有一个线程(称为主线程),也可以有多个线程,每个线程都有自己的一组寄存器、程序计数器和堆栈。下图将显示它们的区别:
另一方面,多进程是在单个计算机系统中使用两个或多个 CPU 单元。我们的主要目标是充分发挥硬件的潜能。为此,我们需要利用计算机系统中可用的所有 CPU 内核。多进程是实现此目标的最佳方法。
Python 是最流行的编程语言之一。以下是一些使其适合并发应用程序的原因:
语法糖
语法糖是在编程语言中设计的语法,旨在使代码更易于阅读或表达。它使语言对人类来说更“甜蜜”:可以更清晰、更简洁地表达事物,或者根据偏好采用替代风格。Python 带有魔术方法,可以定义为对对象进行操作。这些魔术方法用作语法糖,并绑定到更易于理解的关键字。
大型社区
Python 语言在从事 AI、机器学习、深度学习和定量分析领域工作的数据科学家和数学家中获得了巨大的采用率。
用于并发编程的有用 API
Python 2 和 3 有大量专用于并行/并发编程的 API。最流行的是threading、concurrent.features、multiprocessing、asyncio、gevent 和 greenlets 等。
Python 在实现并发应用程序方面的局限性
Python 在并发应用程序方面存在一个限制。这个限制被称为GIL(全局解释器锁)存在于 Python 中。GIL 从不允许我们利用 CPU 的多个内核,因此我们可以说 Python 中没有真正的线程。我们可以理解 GIL 的概念如下:
GIL(全局解释器锁)
这是 Python 世界中最具争议的话题之一。在 CPython 中,GIL 是互斥锁——互斥锁,它使事情线程安全。换句话说,我们可以说 GIL 阻止多个线程并行执行 Python 代码。一次只能由一个线程持有该锁,如果我们想执行一个线程,则它必须首先获取该锁。下图将帮助您了解 GIL 的工作原理。
但是,Python 中有一些库和实现,例如Numpy、Jpython 和IronPytbhon。这些库无需与 GIL 进行任何交互即可工作。