代码之家  ›  专栏  ›  技术社区  ›  Jonathan Holloway

Python中的线程[关闭]

  •  74
  • Jonathan Holloway  · 技术社区  · 15 年前

    用Python编写多线程应用程序所使用的模块有哪些?我了解该语言提供的基本并发机制,以及 Stackless Python ,但他们各自的长处和短处是什么?

    7 回复  |  直到 9 年前
        1
  •  117
  •   Community miroxlav    4 年前

    按照复杂性增加的顺序:

    使用 threading module

    赞成的意见:

    • 在其内部运行任何函数(实际上任何可调用的函数)都非常容易 自己的线程。
    • 共享数据即使不容易(锁也不容易:),至少 最不简单。

    欺骗:

    • 如上所述 by Juergen Python线程实际上不能同时访问解释器中的状态(有一个大锁,臭名昭著的 Global Interpreter Lock )这在实践中意味着线程对于I/O绑定的任务(联网、写入磁盘等)很有用,但对于并发计算却毫无用处。

    使用 multiprocessing 单元

    在简单的用例中,这与使用 threading 除非每个任务都在自己的进程中运行,而不是在自己的线程中运行。(几乎是字面意思:如果你 Eli's example 穿线 具有 multiprocessing , Thread 具有 Process Queue (模块)带有 multiprocessing.Queue ,它应该运行正常。)

    赞成的意见:

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

    欺骗:

    • 进程比线程慢。

    使用事件模型,例如 Twisted

    赞成的意见:

    • 您可以非常精确地控制优先级,控制何时执行。

    欺骗:

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

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

        2
  •  104
  •   Alex Martelli    15 年前

    你已经得到了各种各样的答案,从“假线程”到外部框架,但我没看到有人提到 Queue.Queue --CPython线程的“秘密酱汁”。

    multiprocessing --但它也有自己的特点 Queue 实现,所以您可以在需要注意的情况下应用我给出的一般建议;-),Python内置的 threading 可以。。。但如果你使用它,它会做得更好 慎重地 ,例如,如下所示。

    “忘记”共享内存,它被认为是线程与多处理的主要优势——它不能很好地工作,不能很好地扩展,从来没有,将来也不会。仅对设置一次的数据结构使用共享内存 之前 您生成了子线程,之后再也没有更改过——对于其他所有内容,请创建一个 仅有一个的 线程负责该资源,并通过 队列 .

    不要 在需要时生成线程,否则线程切换开销将使您无法承受。

    两个线程之间的通信始终通过 排队,排队 一种消息传递的形式,是多处理的唯一明智的基础(除了事务存储器之外,它是有前途的,但是除了Haskell之外,我知道它没有任何有价值的实现)。

    每个管理单个资源(或小型内聚资源集)的专用线程侦听特定队列上的请求。队列实例。池中的线程在单个共享队列上等待 让你在这方面失败)。

    deferred 顺便说一句,s非常擅长组织这种结构化的响应。

    您还可以使用队列“停放”资源实例,这些资源可由任何一个线程使用,但决不能在多个线程之间同时共享(与某些DBAPI组件的DB连接、与其他组件的游标等)——这允许您放宽专用线程的要求,以支持更多的池(从共享队列中获取需要可排队资源的请求的池线程将从apppropriate队列中获取该资源,如有必要,等待等)。

    Twisted实际上是组织这种小步舞曲(或方块舞,视情况而定)的一种好方法,这不仅要归功于延迟,还要归功于其健全、坚实、高度可扩展的基础架构:只有在真正需要时,您才可以安排使用线程或子进程,虽然在单个事件驱动线程中执行大多数通常被认为是值得执行的任务。

    但是,我意识到Twisted并不适合所有人——即使你不能完全理解异步事件驱动的方法,也可以使用“专用或共享资源,使用wazoo队列,从不做任何需要锁的事情,或者,Guido禁止,任何更高级的同步过程,如信号量或条件”的方法,与我偶然发现的任何其他广泛应用的线程方法相比,它仍将提供更高的可靠性和性能。

        3
  •  22
  •   Eli Courtwright    15 年前

    这取决于你想做什么,但我倾向于只使用 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()
    
        4
  •  13
  •   Glorfindel DataWraith    5 年前

    Kamaelia 是一个python框架,用于构建具有大量通信进程的应用程序。

    (来源: kamaelia.org ) Kamaelia-并发变得有用、有趣

    在Kamaelia中,您可以从 相互通信的简单组件 . 这加快了开发速度,极大地帮助了维护,也意味着 任何 开发人员,包括新手。这也让它变得有趣:)

    什么样的系统?网络服务器、客户端、桌面应用程序、基于pygame的游戏、转码系统和管道、数字电视系统、垃圾邮件消除器、教学工具,以及大量其他工具:)

    Twisted Parallel Python 然后给出了卡迈利亚的实际演示。

    Easy Concurrency with Kamaelia - Part 1 (59:08)
    Easy Concurrency with Kamaelia - Part 2 (18:15)

        5
  •  6
  •   Cyril Gandon niktrs    13 年前

    关于Kamaelia,上面的答案并没有真正涵盖这里的好处。Kamaelia的方法为处理线程、生成器和;在单个系统中处理并发性。

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

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

    Pipeline(Producer(), Consumer() )
    

    在本例中,不管这些是线程化组件还是其他组件,从使用角度来看,它们之间的唯一区别是组件的基类。生成器组件使用列表进行通信,线程组件使用队列。队列和基于进程的组件使用os.pipes进行通信。

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

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

    显然,并不是每段代码都能以消息传递和共享的方式编写,这就是为什么Kamaelia也有一个简单的软件事务内存(STM),这是一个非常好的主意,有一个讨厌的名字——它更像是变量的版本控制——即检出一些变量,更新它们并提交回来。如果发生碰撞,请冲洗并重复。

    无论如何,我希望这是一个有用的答案。FWIW,Kamaelia设置背后的核心原因是使并发更加安全&更易于在python系统中使用,无需摇尾乞怜。(即一大桶组件

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

    这就是我的说法,请注意这个答案在定义上是有偏见的,但对我来说,Kamaelia的目的是尝试总结IMO的最佳实践。我建议尝试一些系统,看看哪个适合你。(如果这不适用于堆栈溢出,对不起-我是这个论坛的新手:-)

        6
  •  4
  •   Juergen    15 年前

    一个完整的在线游戏(massivly multiplayer)是围绕Stackless及其多线程原理构建的——因为最初的游戏只是为了降低游戏的massivly multiplayer属性。

    CPython中的线程被广泛禁止。一个原因是GIL(一个全局解释器锁),它为执行的许多部分序列化线程。我的经验是,以这种方式创建快速应用程序确实很困难。在我的示例编码中,所有线程的速度都比较慢——只有一个内核(但许多等待输入的操作应该可以提高性能)。

        7
  •  3
  •   Mark Rushakoff    15 年前

    如果你真的想弄脏你的手,你可以试试 using generators to fake coroutines . 就所涉及的工作而言,它可能不是最有效率的,但是协同程序确实为您提供了非常好的控制 合作社 多任务处理,而不是先发制人的多任务处理。

    你会发现一个优点是,总的来说,在使用协作式多任务处理时,你不需要锁或互斥,但对我来说更重要的优点是“线程”之间的切换速度几乎为零。当然,据说Stackless Python在这方面也很好;还有Erlang,如果没有的话 成为Python。

    协作式多任务处理的最大缺点可能是通常缺乏阻止I/O的变通方法。在伪造的协作程序中,您还将遇到一个问题,即除了线程内堆栈的顶层之外,您无法从任何地方切换“线程”。

    在您使用伪协程创建了一个稍微复杂的应用程序之后,您将真正开始欣赏在操作系统级别进行进程调度的工作。