Skip to content

Latest commit

 

History

History
77 lines (43 loc) · 8.85 KB

the-why-when-and-how-of-using-python-multi-threading-and-multi-processing-afd1b8a8ecca.md

File metadata and controls

77 lines (43 loc) · 8.85 KB

使用 Python 多线程和多处理的原因、时间和方式

原文:https://pub.towardsai.net/the-why-when-and-how-of-using-python-multi-threading-and-multi-processing-afd1b8a8ecca?source=collection_archive---------0-----------------------

本指南旨在解释为什么 Python 中需要多线程和多处理,何时使用其中一个,以及如何在程序中使用它们。作为一名人工智能研究人员,我在为我的模型准备数据时广泛使用它们!

图片来自 PixabayParker_West

很久以前,在一个遥远的星系里…

一个聪明而强大的巫师住在一个偏僻的小村庄里。让我们叫他 Dumbledalf。他不仅智慧和强大,而且他也乐于帮助任何请求帮助的人,这意味着人们会从四面八方向巫师寻求帮助。我们的故事开始于一个晴朗的日子,一个年轻的旅行者带给巫师一个魔法卷轴。旅行者不知道卷轴上有什么,但他知道如果有人能破译卷轴上的秘密,那一定是伟大的巫师丹布尔达夫。

第 1 章:单线程、单进程

如果你还没有猜到,我的这个相当伤感的比喻是在谈论 CPU 及其功能。我们的向导是 CPU,而神奇的卷轴是一系列的URL,它引导出 Python 的力量和运用这种力量的知识。

巫师毫不费力地破译了卷轴,他的第一个想法是派他信任的朋友*(哈拉贡?我知道,我知道,那太可怕了)*到卷轴上给出的每一个位置去看看,把他能找到的东西带回来。

如您所见,我们只是使用一个for loop一个接一个地缓慢通过*URL 并读取响应。感谢%%timeIPython 的魔力,*我们可以看到,在我糟糕的互联网上,它只需要 12 秒。

第 2 章:多线程

巫师的智慧在这片土地上广为人知,他很快就想出了一个更有效的方法。与其按顺序派一个人去每个地点,为什么不召集一群(值得信赖的)人,同时分别派他们去每个地点,!一旦他们都回来了,巫师可以简单地组合他们带来的所有东西。

没错,我们可以使用multithreading同时访问多个URL,而不是逐个遍历列表。

好多了!几乎像..魔法。使用多线程可以显著提高许多 IO 绑定 任务的速度。这里,读取URL所花费的大部分时间是由于网络延迟。 IO 绑定 程序大部分时间都在等待,你猜对了,输入/输出*(类似于向导需要等待他的一个/多个朋友去卷轴中给定的位置然后回来)*。这可能是来自网络、数据库、文件甚至用户的 I/O。这种 I/O 往往会花费大量的时间,因为源本身可能需要在传递 I/O 之前执行自己的处理。例如,CPU 的工作速度比网络连接传送数据的速度快得多(想想 Flash 与您的祖母)。

注意: *Multithreading* 在网页抓取这样的任务中非常有用。

第 3 章:多重处理

随着岁月的流逝,我们巫师的名声越来越大,一个相当令人讨厌的黑巫师(萨鲁铎)也受到了嫉妒?沃尔德曼。).在狡猾和嫉妒的驱使下,黑巫师对邓布利达尔夫施了一个可怕的诅咒。一旦诅咒被解除,丹布尔达尔夫知道他只有很短的时间来打破它。绝望中,他翻看了他的咒语书,发现了一个看起来可能有用的反咒语。唯一的问题是,它要求他计算 1000000 以下所有质数的总和。奇怪的咒语,但事实就是如此。

现在,向导知道如果有足够的时间,计算值将是微不足道的,但时间对他来说不是奢侈品。尽管他是个巫师,但他也受到人性的限制,一次只能计算一个数字。如果他要一个一个地把质数加起来,那要花太长时间了。在逆转诅咒还剩几秒钟的时候,他突然想起了几年前从魔法卷轴中学到的multiprocessing咒语。这个咒语可以让他复制自己,并且把复制的数字分开可以让他同时检查多个数字是否是质数。最后,他要做的就是把他和他的复制品发现的所有质数加起来。

由于现代 CPU 通常拥有不止一个内核,我们可以通过使用multiprocessing模块来加速 CPU 绑定的 任务。 CPU 受限 任务是将大部分时间花在 CPU 中执行计算(数学计算、图像处理等)的程序。).如果计算可以彼此独立地执行,我们可以在可用的 CPU 内核之间进行分配,从而显著提高处理速度。

你要做的就是:

  1. 定义要应用的功能
  2. 准备要应用该功能的项目列表
  3. 使用multiprocessing.Pool生成进程。传递给Pool()的数字将是产生的进程数。嵌入在一个with语句中确保了进程在完成执行后被终止。
  4. 使用池进程的map函数组合输出。map功能的输入是应用于每个项目的功能和项目列表。

注意:可以定义函数,以便执行任何可以并行完成的任务。例如,该函数可能包含将计算结果写入文件的代码。

那么,为什么我们需要分开的multiprocessingmultithreading?如果你试图使用multithreading来提高一个 CPU 绑定的 任务的性能,你可能会注意到你实际得到的是一个 性能下降 。异端!让我们看看为什么会这样。

就像巫师被他的人性所限制,一次只能计算一个数字一样,Python 自带了一个叫做全局解释器锁(GIL)的东西。 Python 会很乐意让你产生尽可能多的threads,但是 GIL 确保在任何给定的时间,那些threads中只有一个会被执行

对于一个与 IO 相关的任务来说,这完全没问题。一个thread向一个 URL 发出请求,当它等待响应时,那个thread 可以被另一个thread替换,后者向另一个 URL 发出另一个请求。因为在收到响应之前thread不需要做任何事情,所以在给定时间只有一个thread在执行并不重要。

对于一个受 CPU 限制的 任务来说,拥有多个threads就像胸甲上的乳头一样有用。因为一次只有一个thread被执行,即使你产生了多个threads,每个都有自己的数来检查质数*,CPU 仍然一次只处理一个thread。实际上,这些数字仍然会被一个接一个地检查。如果在 CPU 受限的*任务中使用multithreading,处理多个threads的开销会导致性能下降。**

为了避开这个“限制”,我们使用了multiprocessing模块。multiprocessing不使用threads,而是使用多个processes。每个process都有自己的解释器和内存空间,所以 GIL 不会有所保留。本质上,每个process使用不同的 CPU 内核同时处理不同的数字甜!

您可能会注意到,与使用简单的 for 循环甚至是multithreading相比,使用multiprocessing时 CPU 的利用率要高得多。这是因为您的程序使用了多个 CPU 内核,而不仅仅是一个内核。这是好事!

请记住,multiprocessing有自己的管理多个processes的开销,这通常比multithreading的开销更大。(Multiprocessing衍生出一个单独的解释器,给每个process分配一个单独的内存空间,所以咄!).这意味着,根据经验,如果可以的话,最好使用轻量级的multithreading(阅读: IO 绑定的 任务)。当 CPU 处理成为你的瓶颈时,一般就是召唤multiprocessing模块的时候了。但是请记住,权力越大,责任越大。

如果你产生的processes超过了你的 CPU 一次可以处理的数量,你会发现你的性能开始下降。这是因为操作系统现在必须做更多的工作来交换进出 CPU 内核,因为你有更多的内核。现实可能比简单的解释更复杂,但这是基本的想法。当我们到达 16 processes时,您可以在我的系统上看到性能下降。这是因为我的 CPU 只有 16 个逻辑核心。

第四章:TLDR;

  • 对于 IO 绑定的 任务,使用multithreading可以提高性能。
  • 对于 IO 绑定的 任务,使用multiprocessing也能提高性能,但开销往往比使用multithreading高。
  • Python GIL 意味着在一个 Python 程序中,任何给定时间只能执行一个thread
  • 对于 CPU 绑定的 任务,使用multithreading实际上会恶化性能。
  • 对于 CPU 绑定 的任务,使用multiprocessing可以提高性能。
  • 巫师太牛逼了!

对 Python 中的multithreadingmultiprocessing的介绍到此结束。勇往直前去征服吧!