如何在Python中可用的不同并发方法之间进行选择?

如何在Python中可用的不同并发方法之间进行选择?,python,concurrency,Python,Concurrency,Python中有不同的并发方式,下面是一个简单的列表: 基于进程的:process.Popen,multiprocessing.process,老式的os.system,os.Popen,os.exe* 基于线程:threading.thread 基于微线程的:greenlet 我知道基于线程的并发和基于进程的并发之间的区别,并且我知道一些(但不是太多)关于GIL在CPython的线程支持方面的影响 对于想要实现某种程度的并发性的初学者,如何在两者之间进行选择?或者,它们之间的一般区别是什么

Python中有不同的并发方式,下面是一个简单的列表:

  • 基于进程的:
    process.Popen
    multiprocessing.process
    ,老式的
    os.system
    os.Popen
    os.exe*
  • 基于线程:
    threading.thread
  • 基于微线程的:
    greenlet
我知道基于线程的并发和基于进程的并发之间的区别,并且我知道一些(但不是太多)关于
GIL
CPython
的线程支持方面的影响

对于想要实现某种程度的并发性的初学者,如何在两者之间进行选择?或者,它们之间的一般区别是什么?在Python中还有其他并发的方法吗


我不确定我问的问题是否正确,请随意改进这个问题。

这三种机制都存在的原因是它们有不同的优缺点

首先,如果您有大量独立的小任务,并且没有合理的方法将它们成批处理(通常,这意味着您正在编写服务器,但这不是唯一可能的情况),那么微线程将轻而易举地获胜。在一切陷入停滞或失败之前,您只能运行几百个操作系统线程或进程。所以,要么使用微线程,要么放弃自动并发,开始编写显式回调或协同路由。这确实是微线程唯一一次获胜;否则,它们就像操作系统线程一样,只是有几件事情不能正常工作

接下来,如果您的代码是,那么您需要流程。微线程本质上是单核解决方案;Python中的线程通常不能很好地并行化,因为GIL;进程获得操作系统所能处理的最大并行度。因此,进程将使您的4核系统以4倍的速度运行代码;没有别的办法。(事实上,你可能想走得更远,在不同的计算机之间分发,但你没有问这个问题。)但如果你的代码是这样的话,核心并行就没有帮助了,所以线程和进程一样好

如果你有很多共享的、可变的数据,事情会变得很艰难。流程需要明确地将所有内容放入可共享的结构中,比如使用代替
列表
,这会变得非常复杂。线程自动共享所有内容,这意味着到处都有竞争条件。这意味着您需要非常仔细地思考您的流程,并有效地使用锁。有了流程,经验丰富的开发人员可以构建一个系统,该系统可以处理所有测试数据,但每次给它一组新的输入时都必须重新组织。有了线程,一个经验丰富的开发人员可以编写运行数周的代码,然后意外地、悄悄地扰乱每个人的信用卡号

这两个问题中哪一个更让你害怕,就做那一个,因为你更了解这个问题。或者,如果可能的话,退一步,尝试重新设计代码,使大部分共享数据独立或不可变。这可能是不可能的(如果不让事情变得太慢或太难理解的话),但在决定之前要仔细考虑一下

如果有大量独立数据或共享的不可变数据,线程显然会获胜。进程需要显式共享(如
multiprocessing.Array
)或封送处理<代码>多处理及其第三方替代方案使得封送处理对于所有内容都可拾取的简单情况非常容易,但它仍然不如直接传递值那么简单,而且速度也慢得多

不幸的是,大多数需要传递大量不可变数据的情况与需要CPU并行性的情况完全相同,这意味着需要权衡。对于这种权衡,最好的答案可能是目前4核系统上的操作系统线程,但2年内16核系统上的进程。(如果您将事情组织在一起,例如,或,并在稍后切换到
Pool
ProcessPoolExecutor
,甚至使用运行时配置开关,这几乎解决了问题。但这并不总是可能的。)


最后,如果您的应用程序内在地需要一个事件循环(例如,GUI应用程序或网络服务器),请首先选择您喜欢的框架。比如说,使用PySide与wx编码,或者使用twisted与gevent编码,比使用微线程与操作系统线程编码有更大的区别。一旦你选择了框架,看看你能在多大程度上利用它的事件循环,你认为你需要真正的并发性。例如,如果您需要一些代码每30秒运行一次,不要为此启动线程(micro或OS),请让框架按照自己的意愿进行调度。

听起来您知道不同类型并发之间的差异,在这种情况下,您已经知道问题的答案。此外,一个更容易回答的问题将确定具体情况,以便在两者之间进行选择。有很多网站都在讨论并发方法之间的一般区别,作为旁注:您不应该使用
os.*
方法进行基于进程的并发。如果
多处理
(或类似的第三方模块)不是您想要的,并且您只想执行子进程,请使用
子进程
模块+1到@lxop。在我们开始回答这个问题之前,我们需要知道的关键问题是,您是否希望受到IO限制或CPU限制,您需要在执行线程之间共享什么样的数据(用这个术语来笼统地表示这三个线程),以及您是否出于某种原因已经固有地需要一个主循环(例如,GUI或网络服务器,在这种情况下,答案可能是“以上任何一项都不需要,在单线程事件循环中执行”)@abarnert我认为你的评论将是对这个问题的一个很好的回答