我想并行化我的Python程序,以便它可以在运行它的机器上使用多个处理器。我的并行化非常简单,因为程序的所有并行“线程”都是独立的,并将它们的输出写入单独的文件。我不需要线程来交换信息,但我必须知道线程何时完成,因为我的管道的某些步骤取决于它们的输出。
可移植性非常重要,因为我希望在Mac,Linux和Windows上运行任何Python版本。鉴于这些约束,哪个是最适合实现它的Python模块?我试图在线程,子进程和多处理之间做出决定,这些都似乎提供了相关的功能。
对此有何想法?我想要最简单的便携式解决方案。
答案 0 :(得分:162)
对我而言,这实际上非常简单:
subprocess
用于运行其他可执行文件 ---它基本上是os.fork()
和os.execve()
的包装,并支持可选的管道(在子进程中设置PIPE(显然可以使用其他进程间通信(IPC)机制,例如套接字,SysV共享内存和消息队列)但是你将被限制在任何接口上您正在呼叫的节目支持和IPC频道。
通常一个人同步使用subprocess
---只需调用一些外部实用程序并读回其输出或等待其完成(也许从临时文件中读取结果,或者在将它们发布到某些文件后)数据库)。
然而,可以产生数百个子进程并轮询它们。我个人最喜欢的实用程序classh就是这样做的。 subprocess
模块的最大缺点是它的I / O支持通常是阻塞的。有一个草案PEP-3145可以在Python 3.x的某个未来版本和另一个asyncproc中修复它(警告可以直接下载,而不是任何类型的文档或README)。我还发现直接导入fcntl
并直接操纵Popen
PIPE文件描述符相对容易 - 虽然我不知道这是否可移植到非UNIX平台。
subprocess
几乎没有事件处理支持 ... 虽然您可以使用signal
模块和普通的老式UNIX / Linux信号---轻轻地杀死你的过程。
multiprocessing
用于在现有(Python)代码中运行函数,支持此系列进程之间更灵活的通信。特别是尽可能在模块的multiprocessing
对象周围构建Queue
IPC,但您也可以使用Event
个对象和其他各种功能(某些其中大概是在支持足够的平台上围绕mmap
支持构建的。
Python的multiprocessing
模块旨在提供非常类似于 threading
的接口和功能,同时允许CPython在多个CPU /核心之间扩展处理尽管有GIL(全球翻译锁)。它利用了操作系统内核开发人员完成的所有细粒度SMP锁定和一致性工作。
threading
适用于I / O绑定的相当窄范围的应用程序<(不需要跨多个CPU核心扩展),并且受益于极低的线程切换(使用共享核心内存)与进程/上下文切换的延迟和切换开销。在Linux上,这几乎是空集(Linux进程切换时间非常接近其线程切换)。
threading
遭受 Python中的两个主要缺点。
当然,其中一个是特定于实现的 - 主要影响CPython。这就是GIL。在大多数情况下,大多数CPython程序不会受益于两个以上CPU(内核)的可用性,并且性能通常会从GIL锁定争用中受到 。
不是特定于实现的更大问题是线程共享相同的内存,信号处理程序,文件描述符和某些其他OS资源。因此,程序员必须非常小心对象锁定,异常处理和代码的其他方面,这些方面既微妙又可以杀死,停止或死锁整个进程(线程套件)。
通过比较,multiprocessing
模型为每个进程提供了自己的内存,文件描述符等。其中任何一个中的崩溃或未处理的异常只会杀死该资源并强有力地处理子进程或兄弟进程的消失可以比调试,隔离和修复或解决线程中的类似问题容易得多。
threading
与主要Python系统一起使用,例如NumPy,可能会受到GIL争用的影响,因此大多数自己的Python代码都会受到影响。这是因为他们&# 39;已经专门设计用于这样做。)值得注意的是,Twisted提供了另一种选择,它既优雅又非常难以理解。基本上,冒着过度简化的风险,Twisted的粉丝可能会用干草叉和火把冲击我的家,Twisted在任何(单一)过程中提供和事件驱动的合作多任务。
要了解这是如何实现的,应该阅读select()
的功能(可以围绕 select()或 poll()构建或类似的OS系统调用)。基本上它都是由操作系统的请求在文件描述符列表中的任何活动或某些超时之前进行休眠的能力驱动的。
从这些对select()
的调用中唤醒是一个事件---一个涉及在某些套接字或文件描述符上可用(可读)的输入,或者某些其他(可写)描述符上可用的缓冲空间或套接字,一些例外条件(例如TCP带外PUSH&#39; d包)或TIMEOUT。
因此,Twisted编程模型是围绕处理这些事件而构建的,然后循环在生成的&#34; main&#34;处理程序,允许它将事件分派给处理程序。
我个人认为名称 Twisted 是编程模型的回忆......因为从某种意义上说,问题的解决方法必须是&#34 ;扭曲&#34;反了。您不是将程序视为对输入数据和输出或结果的一系列操作,而是将程序编写为服务或守护程序,并定义它对各种事件的反应。 (事实上,扭曲程序的核心&#34;主循环&#34;是{通常?总是?)a reactor()
。
使用Twisted 的主要挑战涉及围绕事件驱动模型扭曲思维,并避免使用任何未写入在Twisted框架内合作的类库或工具包。这就是为什么Twisted提供自己的模块用于SSH协议处理,curses,以及它自己的子进程/ popen函数,以及许多其他模块和协议处理程序,乍一看似乎会复制Python标准库中的东西。
我认为即使您从未打算使用它,在概念层面理解Twisted也很有用。它可以深入了解线程,多处理甚至子进程处理中的性能,争用和事件处理,以及您进行的任何分布式处理。
(注意:较新版本的Python 3.x包括asyncio(异步I / O)功能,例如 async def , @ async.coroutine 装饰器,等待关键字,来自未来支持。所有这些大致类似于扭曲从一个过程(合作多任务)的角度来看)。
您尚未询问的另一个处理领域,但值得考虑的是 分布式 处理。有许多用于分布式处理和并行计算的Python工具和框架。就个人而言,我认为最容易使用的是最不常被认为是在那个空间中的那个。
围绕Redis构建分布式处理几乎是微不足道的。整个密钥库可用于存储工作单元和结果,Redis LIST可用作Queue()
类似对象,PUB / SUB支持可用于Event
- 类似处理。您可以散列密钥并使用值,在Redis实例的松散集群中进行复制,以存储拓扑和散列令牌映射,以提供一致的散列和故障转移,以便扩展超出任何单个实例的容量,从而协调您的工作人员和编组数据(pickled,JSON,BSON或YAML)。
当然,当您开始围绕Redis构建更大规模和更复杂的解决方案时,您正在重新实现许多已使用Celery,Apache Spark和{{3}解决的功能}},Hadoop,Zookeeper,etcd等等。这些alll具有用于Python访问其服务的模块。
[更新:如果您考虑使用Python跨分布式系统进行计算密集型考虑,请考虑以下几种资源:Cassandra和IPython Parallel。虽然这些是通用的分布式计算系统,但它们是特别易于访问和流行的子系统数据科学和分析]。
您可以使用Python的各种处理方式,从单线程,简单的同步调用到子流程,轮询子流程池,线程和多处理,事件驱动的协作式多任务,以及分发到分布式处理
答案 1 :(得分:58)
multiprocessing
是一种伟大的瑞士军刀型模块。它比线程更通用,因为您甚至可以执行远程计算。因此,这是我建议您使用的模块。
subprocess
模块还允许您启动多个进程,但我发现使用它比新的多处理模块更不方便。
线程是众所周知的微妙,并且,使用CPython,你通常只限于一个核心(尽管如其中一条评论所述,全局解释器锁(GIL)可以在C代码中发布Python代码)。
我相信您引用的三个模块的大部分功能都可以以独立于平台的方式使用。在可移植性方面,请注意multiprocessing
仅在Python 2.6之后才出现在标准版本中(尽管存在一些旧版Python的版本)。但这是一个很棒的模块!
答案 2 :(得分:5)
在类似的情况下,我选择了单独的进程和通过网络套接字进行的一些必要的通信。它是高度可移植的,使用python非常简单,但可能不是更简单(在我的情况下,我还有另一个约束:与用C ++编写的其他进程的通信)。
在你的情况下,我可能会选择多进程,因为python线程,至少在使用CPython时,不是真正的线程。好吧,它们是本机系统线程,但是从Python调用的C模块可能会也可能不会释放GIL,并允许其他线程在调用阻塞代码时运行。
答案 3 :(得分:4)
要在CPython中使用多个处理器,您的仅选项是multiprocessing
模块。 CPython会锁定它的内部(GIL),这会阻止其他cpus上的线程并行工作。 multiprocessing
模块创建新进程(如subprocess
)并管理它们之间的通信。
答案 4 :(得分:1)
Shell出来让unix出去做你的工作:
使用iterpipes包装子进程然后:
INPUTS_FROM_YOU | xargs -n1 -0 -P NUM ./process #NUM parallel processes
OR
Gnu Parallel也将提供
当你派出幕后男孩去做你的多核工作时,你和GIL一起玩。