首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python中的线程

问Python中的线程
EN

Stack Overflow用户
提问于 2009-07-27 19:40:50
回答 6查看 64.2K关注 0票数 77

用Python编写多线程应用程序需要哪些模块?我知道该语言和Stackless Python提供的基本并发机制,但它们各自的优点和缺点是什么?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2009-07-27 21:53:28

按照复杂度递增的顺序:

使用threading module

优点:

在自己的thread.

  • Sharing数据中运行任何函数(实际上是任何可调用的)都很容易(
  • 从来都不容易:),至少很简单。

缺点:

正如前面提到的

  • ,by Juergen Python线程实际上不能在解释器中并发访问状态(有一个很大的锁,臭名昭著的Global Interpreter Lock)。实际上,这意味着线程对于I/O受限的任务(联网、写入磁盘等)很有用,但对于执行并发computation.

一点用处都没有

使用multiprocessing模块

在简单的用例中,这看起来与使用threading完全相同,只是每个任务都在自己的进程中运行,而不是在自己的线程中运行。(如果您使用Eli's example,并将threading替换为multiprocessing,将Thread替换为Process,将Queue (模块)替换为multiprocessing.Queue,那么它应该运行得很好。)

优点:

所有任务的全局解释器实际并发性(没有全局解释器Lock).

  • Scales到多个处理器,甚至可以扩展到多台计算机。

缺点:

与threads.

  • Data相比,
  • 进程更慢,进程之间的共享比不隐式共享的threads.
  • Memory更棘手。您要么必须显式地共享它,要么必须选择变量并来回发送它们。这更安全,但也更难。(如果它越来越重要,Python开发人员似乎正在将人们推向这个方向。)

使用事件模型,如Twisted

优点:

  • 您可以非常精细地控制优先级,以及何时执行什么操作。

缺点:

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

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

票数 119
EN

Stack Overflow用户

发布于 2009-07-28 05:43:42

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

扩展:只要您不需要重叠纯Python CPU繁重的处理(在这种情况下,您需要multiprocessing --但它也有自己的Queue实现,所以您可以根据需要谨慎地应用我给出的一般建议;-),Python的内置threading就可以……但是,如果你仔细使用它,它会做得更好,例如,如下所示。

“忘记”共享内存,被认为是线程与多处理的主要优势--它工作得不好,伸缩性也不好,从来没有,将来也不会。只将共享内存用于那些在派生子线程之前设置一次且此后再也不会更改的数据结构--对于其他所有情况,请让单个线程负责该资源,并通过Queue与该线程通信。

为您通常认为要通过锁保护的每个资源指定一个专门的线程:可变的数据结构或其中的内聚组、到外部进程( DB、XMLRPC服务器等)的连接、外部文件等。为不具有或不需要此类专用资源的通用任务准备一个小的线程池--不要在需要时产生线程,否则线程切换开销将使您不堪重负。

两个线程之间的通信总是通过Queue.Queue --一种消息传递的形式,这是多处理的唯一合理基础(除了事务内存,这是很有前途的,但我知道除了在Haskell中没有值得生产的实现)。

每个管理单个资源(或较小的内聚资源集)的专用线程侦听特定Queue.Queue实例上的请求。池中的线程等待一个共享队列( Queue.Queue是可靠的线程安全,在这一点上不会让你失望)。

只需要在某个队列(共享的或专用的)上排队请求的线程无需等待结果就可以这样做,然后继续前进。最终确实需要请求的结果或确认的线程将与它们刚刚创建的Queue.Queue实例进行配对(请求,接收队列),最终,当响应或确认是继续进行所必需的时,它们从接收队列中获取(等待)。一定要准备好获得错误响应以及真实的响应或确认(顺便说一句,Twisted的deferred非常擅长组织这种结构化响应!)

您还可以使用queue来“驻留”资源的实例,这些资源可以由任何一个线程使用,但不能同时在多个线程之间共享(与某些DBAPI组件的DB连接,与其他DBAPI组件的游标等) --这使您可以放宽专用线程要求,以支持更多的池化(从共享队列获取的池线程需要可排队资源的请求将从应用程序队列获取该资源,必要时等待,等等)。

Twisted实际上是组织这种小步舞(或广场舞,视情况而定)的好方法,这不仅要归功于deferreds,还因为它的健全、坚实、高度可伸缩的基础架构:您可以将事情安排为仅在真正必要时使用线程或子进程,同时在单个事件驱动线程中执行大多数通常被认为值得线程的事情。

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

票数 104
EN

Stack Overflow用户

发布于 2009-07-27 19:44:33

这取决于您尝试做什么,但我倾向于只使用标准库中的threading模块,因为它使得获取任何函数并只在单独的线程中运行它变得非常容易。

代码语言:javascript
复制
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模块提供的同步队列进行生产者/消费者设置

代码语言:javascript
复制
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()
票数 22
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1190206

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档