多线程和多处理是实现并发和并行化的两种最常见的方式,但是,没有多少开发人员了解它们之间的区别,并且无法有效地选择何时使用。
在本文中,我们将讨论多线程和多处理之间的区别,以及如何决定使用什么以及如何在 Python 中实现它。
线程是一个独立的执行流程。它本质上可以看作是进程的一个轻量级的独立组件,可以并行运行。线程是操作系统通常提供的功能。一个进程中可以有多个线程,它们共享相同的内存空间,这意味着它们共享要执行的代码和程序中声明的变量。
为了更好地理解这一点,让我们考虑一个现在在您的笔记本电脑上运行的程序的示例。您可能正在阅读这篇文章,并在浏览器中打开了多个选项卡。同时,您可以打开 Spotify 桌面应用程序来听音乐。现在,浏览器和 Spotify 桌面应用程序就像两个不同的进程,可以使用多个进程或线程来实现并行性。因此,浏览器中的不同选项卡可能在不同的线程中运行。同样,Spotify 可以使用一个线程播放音乐,并使用另一个线程从 Internet 下载您最喜欢的歌曲,并使用第三个线程显示用户界面。这称为多线程。
多线程,顾名思义,就是可以同时执行多个线程的任务或操作。它是一种流行的技术,可以同时快速连续地简化多个任务,并有助于在主线程的多个线程之间快速轻松地共享资源。
下图解释了 Python 中的多线程:
Python 是一种线性语言,但是我们可以使用 Threading Python 模块来理解和实现 Python 中多线程的概念。 threading 模块提供了一个直观的 API,可以轻松生成多个线程,当需要更多处理能力时可以使用这些线程。
可以如下图使用:
import threading from queue import Queue import time def testThread(num): print num if __name__ == '__main__': for i in range(5): t = threading.Thread(target=testThread, arg=(i,)) t.start()
在上面的代码片段中, target
用作可调用对象, args
用于向函数传递参数并start
启动线程。
现在,出现了一些有趣的东西——锁。
在编程中经常出现您希望线程能够修改或使用线程共有的变量的情况。但是,要做到这一点,您必须在 Python 中使用称为Lock或Global Interpreter Lock (GIL)的东西。
来自 Python
在 CPython 中,全局解释器 lock或GIL是一个互斥锁,用于保护对 Python 对象的访问,防止多个线程同时执行 Python 字节码。这个锁是必要的,主要是因为 CPython 的内存管理不是线程安全的。
在解释器级别,Python 基本上将指令序列化。为了让任何线程运行任何函数,它必须首先获得一个全局锁。因为一次只能有一个线程获得该锁,解释器最终必须串行执行指令。这种架构使内存管理线程安全,但它根本不能使用多个 CPU 内核。
简单地说,每当一个函数想要使用或修改一个变量时,它都会锁定该变量,这样如果任何其他函数想要使用或修改该特定变量,它就必须等到该变量被解锁。
考虑两个函数,每个函数将一个变量迭代一个。您可以使用锁来确保一个函数可以在另一个函数之前读取变量、运行计算并写回它,这样我们就可以避免数据损坏。
Python 中的线程对于 I/O 操作或网络绑定任务(例如运行脚本)更有帮助,例如在 Web 抓取的情况下,而不是 CPU 密集型任务。另一个例子是Tensorflow ,它使用线程池并行转换数据。
除了这些应用程序之外,图形用户界面 (GUI) 始终使用多线程来使应用程序具有响应性和交互性。一个常见的例子可能是一个文本编辑程序,用户一旦输入文本,它就会显示在屏幕上。这里一个线程负责用户输入,而另一个线程处理任务以显示它。我们可以为更多功能添加更多线程,例如拼写检查、自动完成等。
现在,在详细讨论了线程之后,让我们继续讨论进程。
进程只是正在执行的计算机程序的一个实例。每个进程都有自己的内存空间,用于存储正在运行的指令,以及它需要访问或存储以执行代码的任何数据。因此,与线程相比,生成进程更耗时且更慢。
如前所述,当我们在桌面上运行多个应用程序时,每个应用程序都是一个进程,当同时执行这些进程时,称为多处理。
多处理是处理器同时执行多个不相关任务的能力。它允许您创建可以并发运行的程序,绕过全局解释器锁 (GIL),并使用整个 CPU 内核来高效执行任务。
尽管多处理的概念与多线程有根本的不同,但它们在 Python 中的语法或用法仍然非常相似。与 Threading 模块类似,我们在 Python 中有一个 Multiprocessing 模块,它有助于生成不同的进程,其中每个进程都有自己的 Python 解释器和 GIL。
由于进程不共享相同的内存,它们不能同时修改相同的内存,从而使我们免于陷入死锁或数据损坏的风险。
可以如下图使用:
import multiprocessing def spawn(num): print(num) if __name__ == '__main__': for i in range(5): p = multiprocessing.Process(target=spawn, args=(i,)) p.start() p.join() # this line allows you to wait for processes
正如我们之前所讨论的,如果任务是 CPU 广泛的并且没有任何 I/O 操作或用户交互,则 Mutliprocessing 是一个更明智的选择。
这里有几点总结一下多处理和多线程的区别、优缺点:
我们可以从这次讨论中得出以下结论:
既然您了解了 Python 多处理和多线程的操作方式以及它们的比较方式,您就可以有效地编写代码并在各种情况下应用这两种方法。
我希望你觉得这篇文章有帮助。继续阅读!