在Python中用于编写多线程应用程序的模块有哪些?我知道该语言以及Stackless Python提供的基本并发机制,但是它们各自的优缺点是什么?
asyncio
吗?在此先感谢您,并希望看到代码的外观。
在Python中用于编写多线程应用程序的模块有哪些?我知道该语言以及Stackless Python提供的基本并发机制,但是它们各自的优缺点是什么?
asyncio
吗?在此先感谢您,并希望看到代码的外观。
Answers:
为了增加复杂度:
优点:
缺点:
在简单的用例中,threading
除了每个任务都是在自己的进程而不是自己的线程中运行之外,这与使用完全一样。(几乎从字面上看:如果以Eli的示例为例,并threading
用multiprocessing
,,替换Thread
为Process
,然后将Queue
(模块)替换为multiprocessing.Queue
,它应该可以正常运行。
优点:
缺点:
优点:
缺点:
在所有情况下,我假设您已经了解了多任务处理中的许多问题,尤其是如何在任务之间共享数据的棘手问题。如果由于某种原因您不知道何时以及如何使用锁和条件,则必须从这些开始。多任务代码充满了微妙之处和技巧,在开始之前最好对概念有一个很好的了解。
multiprocessing
会较慢。但是,在这种情况下,您的多线程使用率也可以大大提高。
从“伪线程”一直到外部框架,您已经获得了各种各样的答案,但是我还没有看到有人提及Queue.Queue
CPython线程的“秘密秘诀”。
扩展:只要您不需要重叠纯Python CPU繁重的处理(在这种情况下,您需要multiprocessing
-但它也具有其自己的Queue
实现,因此可以在需要注意的情况下应用我的一般建议'm Give ;-),Python的内置功能threading
可以...但是如果您明智地使用它,它将做得更好,例如,如下所示。
“忘记”共享内存,据说是线程与多处理的主要结合-效果不好,扩展性不好,永远不会,永远不会。使用只适用于设置一次数据结构,共享内存之前的一切,做一个-你产卵子线程,并没有改变事后单一线程负责该资源的同时,通过该线程进行通信Queue
。
为您通常认为要通过锁保护的每个资源分配一个专用线程:可变数据结构或其内聚组,与外部进程(DB,XMLRPC服务器等)的连接,外部文件等,等等。获得一个小的线程池来执行没有或不需要那种专用资源的通用任务-不要在需要时按需生成线程,否则线程切换开销将使您不堪重负。
两个线程之间的通信始终是通过Queue.Queue
-消息传递的形式进行的,这是多处理的唯一理智的基础(除了事务内存之外,这很有希望,但我知道除了In Haskell,没有其他有价值的实现)。
每个管理单个资源(或一组小的内聚资源)的专用线程都侦听特定Queue.Queue实例上的请求。池中的线程在一个共享的Queue.Queue上等待(队列具有可靠的线程安全性,不会在此失败)。
只需要在某个队列(共享队列或专用队列)上排队请求的线程无需等待结果就继续前进。最终确实需要结果或请求确认的线程将它们与刚刚创建的Queue.Queue实例配对(请求,接收队列),并最终在进行响应或确认必不可少时继续执行)从他们的接收队列。确保您已准备好获得错误响应以及真实的响应或确认(Twisted的擅长deferred
组织这种结构化响应,顺便说一句!)。
您还可以使用Queue来“驻留”资源的实例,这些实例可以由任何一个线程使用,但绝不能一次在多个线程之间共享(具有某些DBAPI组件的DB连接,具有其他游标的游标等等)-这使您可以放松支持更多池化的专用线程要求(从共享队列中获取需要可排队资源的请求的池线程将从适当队列中获取该资源,并在必要时等待,等等)。
Twisted实际上是组织此小对象(或视情况而定的广场舞)的一种好方法,这不仅要归功于延迟,而且由于其健全,扎实,高度可扩展的基础体系结构:只有在以下情况下,您才可以安排使用线程或子进程的事物真正有保证,同时在一个事件驱动的线程中执行通常认为线程有价值的大多数事情。
但是,我意识到Twisted并非适合所有人-“专用或池资源,使用wazoo排队,永远不需要做任何需要Lock的操作,或者Guido禁止,任何更高级的同步过程,例如信号量或条件”方法都可以即使您只是无法用异步事件驱动的方法来处理问题,它仍然会被使用,并且仍然会提供比我偶然发现的任何其他广泛应用的线程方法更高的可靠性和性能。
这取决于您要执行的操作,但是我偏爱仅使用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是一个python框架,用于构建具有许多通信流程的应用程序。
这是Pycon 2009的视频。首先将Kamaelia与Twisted和Parallel Python进行比较,然后进行Kamaelia的演示。
(来源:kamaelia.org) Kamaelia-并发变得有用,有趣
在Kamaelia中,您可以使用相互通信的简单组件来构建系统。这样可以加快开发速度,极大地帮助维护,也意味着您可以自然地构建并发软件。任何开发人员(包括新手)都可以使用它。它还使它变得有趣:)
什么样的系统?网络服务器,客户端,桌面应用程序,基于pygame的游戏,代码转换系统和管道,数字电视系统,垃圾邮件消除器,教学工具,以及更多其他内容:)
与Kamaelia轻松并发-第1部分(59:08)
与Kamaelia轻松并发-第2部分(18:15)
关于Kamaelia,以上答案并未真正涵盖这里的收益。Kamaelia的方法提供了一个统一的接口,这种接口在实用性方面并不完美,用于在单个系统中处理线程,生成器和进程以实现并发。
从根本上讲,它提供了一个具有收件箱和发件箱的运行中事物的隐喻。您将邮件发送到发件箱,并将其连接在一起时,邮件将从发件箱流到收件箱。无论您是使用生成器,线程或进程,还是与其他系统对话,此隐喻/ API均保持不变。
“不完美”部分是由于尚未为收件箱和发件箱添加语法糖(尽管此讨论仍在讨论中)-着重于系统的安全性/可用性。
以上面使用裸线程的生产者消费者示例为例,这在Kamaelia中变为:
Pipeline(Producer(), Consumer() )
在此示例中,这些是线程化组件还是其他无关紧要,从使用角度来看,它们之间的唯一区别是该组件的基类。生成器组件使用列表进行通信,使用Queue.Queues使用线程化的组件进行通信,并使用os.pipes进行基于进程的通信。
但是,这种方法背后的原因是使调试错误变得更加困难。在线程化或任何共享内存并发中,面临的第一大问题是意外破坏了共享数据更新。通过使用消息传递,您可以消除一类错误。
如果您在任何地方使用裸线程并进行锁定,那么通常会假设您在编写代码时不会犯任何错误。我们都渴望实现这一目标,但这种情况很少发生。通过将锁定行为包装在一个地方,可以简化可能出错的地方。(上下文处理程序有帮助,但对于上下文处理程序外的意外更新则无济于事)
显然,并非每段代码都可以编写为消息传递和共享样式,这就是为什么Kamaelia也拥有简单的软件事务存储(STM)的原因,这是一个非常简洁的名称,名称很讨厌-更像是变量的版本控制-即检查一些变量,更新它们并提交。如果发生冲突,请冲洗并重复。
相关链接:
无论如何,我希望这是一个有用的答案。FWIW,Kamaliaa设置背后的核心原因是使并发更安全,更易于在python系统中使用,而不会尾巴乱动狗。(即,大量的组件
我能理解为什么其他Kamaelia答案被修改了,因为对我来说,它看起来更像是广告,而不是答案。作为Kamaelia的作者,很高兴看到热情,尽管我希望它包含更多相关的内容:-)
这就是我的说法,请注意,这个答案在定义上是有偏差的,但是对我而言,Kamaliaa的目标是尝试并总结IMO的最佳实践。我建议尝试一些系统,然后看看哪个对您有用。(同样如果这不适合堆栈溢出,抱歉-我是这个论坛的新手:-)
如果您真的想弄脏手,可以尝试使用生成器来伪造协程。就所涉及的工作而言,它可能不是最高效的,但是协程确实为您提供了对协作多任务的非常好的控制,而不是您会在其他地方发现的抢先式多任务。
您会发现一个优点是,使用协作多任务处理时,基本上不需要锁或互斥体,但是对我而言,更重要的优点是“线程”之间的切换速度几乎为零。当然,据说无堆栈Python对此也非常有用。然后是Erlang(如果不是必须使用Python)。
协作式多任务处理中的最大缺点可能是,普遍缺乏阻止I / O的解决方法。在伪造的协程中,您还将遇到一个问题,即您不能从线程中堆栈顶层以外的任何内容切换“线程”。
在使用假协同程序制作了一个甚至稍微复杂的应用程序之后,您将真正开始欣赏在OS级别进行流程调度的工作。