返回介绍

9.1 multiprocessing 模块综述

发布于 2024-01-25 21:44:08 字数 1984 浏览 0 评论 0 收藏 0

multiprocessing模块在Python 2.6中被引入,通过采用已经存在的pyProcessing模块,把它合入Python的内置库集合中。它的主要组件是:

进程

一个当前进程的派生(forked)拷贝,创建了一个新的进程标识符,并且任务在操作系统中以一个独立的子进程运行。你可以启动并查询进程的状态并给它提供一个目标方法来运行。

包装了进程或线程。在一个方便的工作者线程池中共享一块工作并返回聚合的结果。

队列

一个先进先出(FIFP)的队列允许多个生产者和消费者。

管理者

一个单向或双向的在两个进程间的通信渠道。

ctypes

允许在进程派生(forked)后,在父子进程间共享原生数据类型(例如,整型数、浮点数和字节数)。

同步原语

锁和信号量在进程间同步控制流。

 备忘 

在Python 3.2中引入了concurrent.futures模块(通过PEP 3148),它通过一个更简单的基于Java的java.util.concurrent接口提供了multiprocessing的核心行为。它向后兼容于更早的Python版本。我们在这里不去提它,因为它不像multiprocessing那样灵活,但是我们怀疑随着Python 3+越来越多地被采用,我们将看到它有朝一日会取代multiprocessing。

在本章余下部分,我们会介绍一组例子来演示使用这个模块的普遍方法。

我们将使用蒙特卡罗方法由一个进程池或者线程池来估算pi值,使用常规的Python和numpy。这是一个简单问题,具有很好理解的复杂性,所以它能轻松地并行化。我们也能够从使用numpy的线程中看到一个预料之外的结果。接下来,我们会使用相同的池方法来搜索素数。我们会调查搜索素数的不可预测的复杂性,并且看看我们怎样能有效(和无效!)地拆分工作量来最大化地利用我们的计算资源。我们会通过转移到队列来完成素数搜索,在那里我们会引入Process对象来取代池并且使用一个工作和毒药列表来控制工作者的生命周期。

接下来,我们会通过处理进程间通信(IPC)来验证一个小的可能的素数集合。通过在多个CPU之间拆分每一个数字的工作负载,如果找到了一个因子,我们就使用IPC来提早结束搜索,这样我们能够显著地击败单个CPU搜索进程的速度。我们将会涉及共享Python对象、OS原语和一个Redis服务器来调查每一种方法在复杂性和扩展性上面的妥协。

我们可以在4个CPU之间共享一个6.4GB的numpy数组,从而拆分一个巨大的工作负载而不用拷贝数据。如果你有可并行化操作的大数组,那么这个技术应该为你带来巨大的速度提升,因为你可以在RAM中分配更少的空间,拷贝更少的数据。最后,我们会看看在进程间同步存取一个文件和一个变量(作为一个Value)而不破坏数据,从而演示如何正确地锁住共享状态。

 备忘 

PyPy(在第7章讨论过)全面支持multiprocessing库,接下来的CPython例子(尽管在写作时没有numpy的例子)使用PyPy全都运行得快很多。如果你只使用CPython代码(没有C扩展或者更多的复杂库)来做并行处理,那么PyPy可能就会对你快速取胜。

本章(和整本书)集中于Linux上。Linux具有派生(fork)进程,通过克隆父进程来创建新进程。Windows缺少fork,所以multiprocessing模块施加了一些Windows特有的约束,如果你要使用Windows平台,我们劝你要检查一下。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文