在 Python 中用于编写多线程应用程序的模块有哪些?我了解该语言提供的基本并发机制以及 无堆栈Python, ,但是他们各自的优点和缺点是什么?

有帮助吗?

解决方案

按照复杂性递增的顺序:

使用 线程模块

优点:

  • 在自己的线程中运行任何功能(实际上可召唤)真的很容易。
  • 共享数据是不容易的话(锁从来都不是一件容易的事:)至少简单。

缺点:

  • 如上所述 通过于尔根 Python 线程实际上无法同时访问解释器中的状态(有一个大锁,即臭名昭著的锁) 全局解释器锁.) 这在实践中意味着线程对于 I/O 绑定任务(网络、写入磁盘等)很有用,但对于并发计算则毫无用处。

使用 多重处理 模块

在简单的用例中,这看起来与使用完全相同 threading 但每个任务都在自己的进程中运行,而不是在自己的线程中运行。(几乎字面意思是:如果你采取 以利的例子, ,并替换 threadingmultiprocessing, Thread, , 和 Process, , 和 Queue (模块)与 multiprocessing.Queue, ,它应该运行得很好。)

优点:

  • 所有任务的实际并发性(无全局解释器锁)。
  • 扩展到多个处理器,甚至可以扩展到多个 机器.

缺点:

  • 进程比线程慢。
  • 进程之间的数据共享比线程之间的数据共享更棘手。
  • 内存不是隐式共享的。您要么必须显式共享它,要么必须腌制变量并来回发送它们。这更安全,但更困难。(如果它越来越重要的话,Python 开发人员似乎正在推动人们朝这个方向发展。)

使用事件模型,例如 扭曲的

优点:

  • 您可以非常精细地控制优先级、执行时间。

缺点:

  • 即使有一个好的库,异步编程通常也比线程编程更难,无论是在理解应该发生的情况还是调试实际发生的情况方面都困难。

全部 我假设您已经了解与多任务处理相关的许多问题,特别是如何在任务之间共享数据的棘手问题。如果由于某种原因您不知道何时以及如何使用锁和条件,则必须从这些开始。多任务代码充满了微妙之处和陷阱,最好在开始之前充分理解概念。

其他提示

您已经得到一个公平的多种答案,从“假线程”一路外部框架,但我看到没有人提到Queue.Queue - CPython的线程的“秘密武器”

要扩大:只要你不需要重叠纯Python CPU重处理(在这种情况下,你需要multiprocessing - 但它有它自己Queue实现,也因此你可以用一些必要的注意事项申请我给;-)一般建议,Python的内置threading会做...但它会做,如果你使用它更好的深思熟虑的,例如,如下:

“忘记”共享存储器,假想螺纹VS多的主加 - 它不能很好地工作,它不能很好地扩展,从来没有,永远不会。使用共享内存仅适用于之前所建立的数据结构一次的 的你派生子线程,永不改变之后 - 对于一切,作出的的线程负责该资源,并且经由Queue该线程进行通信。

投入了专门的线程来你通常认为由锁来保护的每个资源:一个可变的数据结构或内聚组体,向外部处理(DB,一个XMLRPC服务器等),外部文件的连接,等,等得到一个小的线程池去为那些没有或需要那种专用资源的通用任务 - 在需要的时候的的生成线程的和,或thread-切换开销将压倒你。

两个线程之间

的通信总是经由Queue.Queue - 消息传递,用于多处理唯一明智基础的一种形式(除了事务存储器,这是有希望的,但对于我知道没有生产价值的实现方式的不同之处在Haskell)。

每个专用线程管理的单个资源(或小的内聚的资源集)侦听上的特定Queue.Queue实例请求。在一个池中的线程在单个共享Queue.Queue等待(Queue是牢固线程和不会失败你在此)。

这只是需要排队一些队列(共享或专用)的请求线程这样做而无需等待结果,然后继续前进。线程最终确实需要对请求的结果或确认排队一对(请求,receivingqueue)与他们刚才Queue.Queue的一个实例,最终,当响应或确认才能继续进行,他们得到的是不可缺少的(等待)从他们的receivingqueue。要确保你准备好了错误的反应以及实际响应或确认(Twisted的deferreds都在举办这种结构化的反应很大,BTW!)。

您还可以使用队列为“公园”,其可以通过任何一个线程使用,但永远不会被同时在多个线程之间共享(与一些DBAPI精铸件DB连接,与其他人的游标,等等)资源的实例 - 这个让你放松有利于更多的池(池线程从共享队列中获得一个请求需要一个QUEUEABLE资源将得到资源从apppropriate队列中,如果有必要等待,等等等等)的专用线程的要求。

扭曲实际上是组织这次小步舞曲(或广场舞视情况而定),不仅得益于deferreds一个很好的方式,但由于其稳健,踏实,高度可扩展的基础架构:你可以安排你的事情使用线程或子进程只有当真正的保证,而这样做的大多数事情通常被认为是线程值得在一个事件驱动的主题。

不过,我意识到Twisted是不是对每个人 - 在“专用或共享资源,利用排队的wazoo,什么也不做,需要一个锁定,或禁止圭多,任何同步过程更先进,如信号或条件“办法仍然可以使用,甚至如果你不能环绕异步事件驱动的方法你的头,仍然会提供更多的可靠性和性能比我曾经偶然发现了任何其他广泛适用线程方法。

这取决于你想要做什么,但我偏爱只是用threading模块中的标准库,因为这使得它可以很容易采取的任何功能,只是运行在一个单独的线程。

from threading import Thread

def f():
    ...

def g(arg1, arg2, arg3=None):
    ....

Thread(target=f).start()
Thread(target=g, args=[5, 6], kwargs={"arg3": 12}).start()

等。所以经常使用由Queue模块提供的同步队列生产者/消费者设置

from Queue import Queue
from threading import Thread

q = Queue()
def consumer():
    while True:
        print sum(q.get())

def producer(data_source):
    for line in data_source:
        q.put( map(int, line.split()) )

Thread(target=producer, args=[SOME_INPUT_FILE_OR_SOMETHING]).start()
for i in range(10):
    Thread(target=consumer).start()

Kamaelia 是用于与大量通信进程的构建应用蟒框架。

  

     

<子>(来源: kamaelia.org Kamaelia - 并发制成有用的,有趣的

     

在Kamaelia构建从互相交谈的简单的部件系统。这加快发展,大规模有助于维护和也意味着您打造自然并发软件即可。它的目的是通过的任何显影剂,包括新手访问。这也使得它的乐趣:)

     

什么样的系统?网络服务器,客户端,桌面应用程序,基于Pygame的游戏,转码系统和管道,数字电视系统,垃圾邮件eradicators,教学工具,并有相当数量更多:)

下面是从2009年PYCON它首先通过比较Kamaelia到扭曲和的并行的Python 和然后给出Kamaelia的示范一个手中。

易并发与Kamaelia - 第1部分(59:08),点击 易并发与Kamaelia - 第2部分(18:15)

关于 Kamaelia,上面的答案并没有真正涵盖这里的好处。Kamaelia 的方法提供了一个统一的接口,虽然实用但并不完美,用于在单个系统中处理线程、生成器和进程以实现并发。

从根本上来说,它提供了一个具有收件箱和发件箱的运行事物的隐喻。您将消息发送到发件箱,当连接在一起时,消息从发件箱流到收件箱。无论您使用生成器、线程或进程,还是与其他系统对话,这个隐喻/API 都保持不变。

“不完美”部分是由于尚未为收件箱和发件箱添加语法糖(尽管这正在讨论中) - 系统重点关注安全性/可用性。

以上面使用裸线程的生产者消费者为例,这在 Kamaelia 中变成了这样:

Pipeline(Producer(), Consumer() )

在此示例中,这些组件是否是线程组件并不重要,从使用角度来看,它们之间的唯一区别是组件的基类。生成器组件使用列表进行通信,线程组件使用 Queue.Queues 进行通信,基于进程的组件使用 os.pipes 进行通信。

不过,这种方法背后的原因是让调试错误变得更加困难。在线程或任何共享内存并发中,您面临的第一个问题是意外破坏共享数据更新。通过使用消息传递,您可以消除 错误类别。

如果您在任何地方都使用裸线程和锁,那么您通常会假设编写代码时不会犯任何错误。虽然我们都渴望这一点,但这种情况很少发生。通过将锁定行为集中在一处,您可以简化可能出错的地方。(上下文处理程序有帮助,但对上下文处理程序之外的意外更新没有帮助)

显然,并不是每段代码都可以编写为消息传递和共享风格,这就是为什么 Kamaelia 也有一个简单的软件事务内存(STM),这是一个非常巧妙的想法,但名字很讨厌 - 它更像是变量的版本控制 - 即检查一些变量,更新它们并提交回来。如果发生冲突,请冲洗并重复。

相关链接:

无论如何,我希望这是一个有用的答案。FWIW,Kamaelia 设置背后的核心原因是为了让并发在 Python 系统中更安全、更容易使用,而不是摇尾巴。(即一大桶组件

我可以理解为什么卡马利亚的另一个答案被修改了,因为即使对我来说,它看起来也更像是广告而不是答案。作为 Kamaelia 的作者,很高兴看到大家的热情,尽管我希望其中包含更多相关内容:-)

这就是我的说法,请注意,这个答案从定义上来说是有偏见的,但对我来说,Kamaelia 的目标是尝试概括 IMO 最佳实践。我建议尝试几个系统,看看哪个适合您。(如果这不适合堆栈溢出,抱歉 - 我是这个论坛的新手:-)

我会使用无堆栈的Python微螺纹(微进程),如果我不得不在所有使用线程。

有一个整体的网络游戏(massivly多人)是围绕打造无堆栈及多线程处理原则 - 因为原来只是放慢了比赛的massivly多人属性

在CPython的线程被广泛气馁。其中一个原因是GIL - 一个全球性的解释锁 - 序列化线程的执行的许多地方。我的experiance是,它真的很难创建快速应用这种方式。其中具有螺纹所有慢我的示例值编码 - 与一个核心(但许多等待输入应该已经取得了一些性能提升可能)

使用CPython的,而如果可能的话使用单独的过程。

如果你真的想要得到你的手脏,你可以使用发电机尝试假协同程序的。它可能不是最有效的参与工作而言,协程,但你能提供的非常精细的控制的合作社的多任务处理,而不是抢先式多任务,你会发现在其他地方。

一个好处,你会发现,总的来说,你会不会使用合作多任务处理时,需要锁或互斥的,但对我来说更重要的优势是“线程”之间的几乎为零的切换速度。当然,无堆栈的Python据说是为很好的为好;然后还有二郎,如果没有的的是Python的。

大概在合作的多任务处理的最大的缺点是普遍缺乏变通方法阻塞I / O的。而在伪造协程,你也会碰到,你不能从任何东西,但栈的线程中的最高级别切换“线程”的问题。

您已经和假的协同程序稍微复杂的应用程序后,你会真正开始认识到,在操作系统级别进入进程调度的工作。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top