在Python中的subprocess,多处理和线程之间进行决定?

我想并行化我的Python程序,以便它可以在运行的机器上使用多个处理器。 我的并行化非常简单,因为程序的所有并行“线程”都是独立的,并将其输出写入单独的文件。 我不需要线程来交换信息,但是我知道线程何时完成,因为我的pipe道的某些步骤取决于它们的输出。

可移植性很重要,因为我希望在Mac,Linux和Windows上运行任何Python版本。 鉴于这些限制,哪个是最适合实现这个的Python模块呢? 我试图决定线程,subprocess和多处理,这似乎都提供相关的function。

对此有何想法? 我想要最简单的便携式解决scheme。

multiprocessing是一个伟大的瑞士军刀types的模块。 它比线程更通用,因为您甚至可以执行远程计算。 因此,这是我build议你使用的模块。

subprocess进程模块也允许你启动多个进程,但是我发现它比新的多进程模块更不方便。

线程是非常微妙的,而且,在CPython中,你经常被限制在一个核心上(尽pipe如其中一个注释中所述,全局解释器锁(GIL)可以用Python代码调用的C代码发布) 。

我相信你所引用的三个模块的大部分function都可以以平台无关的方式使用。 在可移植性方面,请注意,自Python 2.6以来, multiprocessing只是标准版本(尽pipePython的某些旧版本确实存在版本)。 但这是一个很好的模块!

对我来说这其实很简单:

subprocess选项:

subprocess os.fork()为了运行其他可执行文件 – 它基本上是os.fork()os.execve()一个包装,它支持可选的pipe道操作(在subprocess之间build立os.execve() (显然其他OS进程间通信IPC)机制,比如套接字,SysV共享内存和消息队列,但是通常你使用subprocess来运行第三方二进制可执行文件,并且会被支持的任何接口和IPC通道卡住。

通常是同步使用subprocess – 只需调用一些外部实用程序,读取其输出或等待其完成(可能从临时文件读取其结果,或者在将其发布到某个数据库之后)。 但是可以产生数百个subprocess并轮询它们。 我个人最喜欢的工具就是这样做的。 subprocess模块最大的缺点是它的I / O支持通常是阻塞的。 有一个草案PEP-3145来解决在Python 3.x的一些未来版本和一个替代的asyncproc (警告,导致正确的下载,而不是任何forms的文档或README)。 我还发现,直接导入fcntl并直接操作Popen PIPE文件描述符是相对容易的 – 虽然我不知道这是否可移植到非UNIX平台。

subprocess 进程几乎没有事件处理的支持 …… 尽pipe你可以使用signal模块和简单的老式UNIX / Linux信号—轻轻地杀死你的进程,就像它一样。

多处理选项:

multiprocessing用于在现有(Python)代码中运行function ,支持进程族之间更灵活的通信。 尤其是,最好在模块的Queue对象周围构buildmultiprocessing IPC,但也可以使用Event对象和其他各种function(其中一些大概是在支持足够平台的mmap支持上构build的)。

Python的multiprocessing模块旨在提供 threading非常相似的接口和function,同时允许CPython在多个CPU /内核之间扩展您的处理,而不pipeGIL如何。 它利用了您的操作系统内核开发人员所做的所有细粒度的SMPlocking和一致性工作。

线程选项:

threading针对I / O绑定 (不需要在多个CPU核心之间扩展) 的相当狭窄的应用范围,并且受益于线程切换(共享核心内存)与进程的极低延迟和切换开销/上下文切换。 在Linux上,这几乎是空集(Linux进程切换时间非常接近线程切换)。

threading 在Python中两个主要的缺点 。 其中一个当然是具体实现—主要影响CPython。 这就是GIL(Global Interpreter Lock)。 在大多数情况下,大多数CPython程序不会从两个以上的CPU(内核)的可用性中受益,并且通常性能将受到 GILlocking争用的影响。 不是实现特定的更大的问题是线程共享相同的内存,信号处理程序,文件描述符和某些其他操作系统资源。 因此,程序员必须非常小心对象锁,exception处理和代码的其他方面,这些方面都是微妙的,可以杀死,阻塞或死锁整个进程(线程套件)。

通过比较, multiprocessing模型为每个进程提供自己的内存,文件描述符等。其中的任何一个崩溃或未处理的exception只会杀死该资源,并且强健地处理subprocess或同级进程的消失可能比debugging更容易,隔离和固定或解决线程中的类似问题。

  • (注意:在主要的Python系统(如Numpy)中使用threading可能会因GIL争用而受到很大的损失,那么大部分自己的Python代码会这样做,这是因为它们是专门devise的。

扭曲的选项:

另外值得注意的是, Twisted提供了另一个既优雅又非常具有挑战性的替代scheme。 基本上,Twisted的粉丝有可能会过分简化,以至于Twisted的粉丝可能会用干草叉和火把砸我的家,Twisted会在任何(单一)过程中提供和事件驱动的合作多任务处理。

要理解这是如何可能的,应该阅读有关select() (可以围绕select()poll()或类似的OS系统调用)的特性。 基本上,所有的驱动都是由OS的请求来hibernate,等待文件描述符列表中的任何活动或超时。

从这些调用select()中的每一个唤醒都是一个事件—或者涉及一些套接字或文件描述符上的可用input(可读),或者某些其他(可写)描述符或套接字上可用的缓冲空间,条件(例如TCP带外PUSH'd数据包)或TIMEOUT。

因此,Twisted编程模型是围绕处理这些事件而build立的,然后在产生的“主”处理程序上循环,从而将事件分派给你的处理程序。

我个人认为这个名字被扭曲成了对编程模型的回忆……因为你在这个问题上的态度必定在某种程度上被“扭曲”了。 您不是将程序设想为对input数据和输出或结果的一系列操作,而是将程序编写为服务或守护进程,并定义它如何对各种事件做出反应。 (实际上Twisted程序的核心“主循环”(通常是“总是”)是一个reactor()

使用Twisted主要挑战包括扭曲事件驱动模型的思维,并且避免使用任何没有写入Twisted框架内合作的类库或工具包。 这就是为什么Twisted提供了自己的SSH协议处理模块,curses和它自己的subprocess / popen函数,以及许多其他模块和协议处理程序,它们似乎在Python标准库中复制了东西。

我认为从概念上理解Twisted是有用的,即使你从不打算使用它。 它可以深入了解线程,多处理甚至subprocess处理以及您所进行的任何分布式处理中的性能,争用和事件处理。

注意:较新版本的Python 3.x包含asynchronous(asynchronousI / O)特性,例如async def@ async.coroutine修饰器, await关键字以及未来支持的产量,所有这些大致类似于从一个过程(合作多任务)的angular度扭曲 )。

分布式选项:

还有一个你没有问过的领域,但是值得考虑的是分布式处理。 有许多用于分布式处理和并行计算的Python工具和框架。 就我个人而言,我认为最容易使用的是最不经常被认为是在这个空间。

围绕Redis构build分布式处理几乎是微不足道的。 整个密钥存储可用于存储工作单元和结果,Redis LIST可以像对象一样用作Queue() ,PUB / SUB支持可用于类Event处理。 您可以对您的密钥进行散列,并使用在松散群集的Redis实例中复制的值来存储拓扑和散列令牌映射,以便提供一致的散列和故障转移,从而扩展超出协调工作人员的任何单个实例的能力并对其中的数据(pickled,JSON,BSON或YAML)进行编组。

当然,随着您开始围绕Redis构build更大规模和更复杂的解决scheme,您将重新实现已经使用Hadoop,Zookeeper,Cassandra等解决的许多function。 那些也有Python访问他们的服务的模块。

[更新:如果您正在考虑将Python用于跨分布式系统的计算密集型,请考虑一些资源: IPython Parallel和PySpark 。 虽然这些是通用的分布式计算系统,但它们是特别易于使用和stream行的子系统,数据科学和分析学]。

结论

在Python中,您可以使用单线程,简单的同步调用到subprocess,轮询subprocess池,线程化和多处理,事件驱动的协同多任务以及分布式处理。

要在CPython中使用多个处理器, 唯一的select是multiprocessing模块。 CPython保持它的内部锁( GIL ),防止其他cpus上的线程并行工作。 multiprocessing模块创build新的进程(如subprocess进程)并pipe理它们之间的通信。

在类似的情况下,我select了单独的进程和通过networking套接字进行必要的通信。 这是非常便携,使用python非常简单,但可能不是更简单(在我的情况下,我还有另一个约束:与C ++编写的其他进程通信)。

在你的情况下,我可能会去使用multithreading,至less在使用CPython时,作为python线程,不是真正的线程。 那么,它们是本机系统线程,但是从Python调用的C模块可能释放或不释放GIL,并允许其他线程在调用阻塞代码时运行它们。

壳出来,让unix出去做你的工作:

使用iterpipes来包装subprocess ,然后:

从特德Ziuba的网站

INPUTS_FROM_YOU | xargs -n1 -0 -P NUM ./process #NUM个并行进程

要么

Gnu并行也将服务

你把GIL挂在外面的时候,把你的多function男孩送出去做多核的工作。