Python中的线程效率有多高?

12

我听说在 Python 中使用线程不如其他编程语言高效。

这是真的吗?如果是,那么 Python 程序员该如何克服这个问题呢?


6
请注意,GIL只存在于CPython中。您仍然可以使用类似IronPython的东西,而不会为使用线程而受到(很多)惩罚。 - Jacob
我听说它非常高效。 - Cosmin
有人可以贴一些关于Python多线程的问题或非问题的链接吗? - Deep Kapadia
1
http://wiki.python.org/moin/GlobalInterpreterLock - Jacob
1
关于线程和Python已经有很多问题了.... - tauran
6个回答

22

人们认为Python中的多线程不太有效率是因为全局解释器锁(Global Interpreter Lock)。由于解释器的编写方式,同时只能有一个线程安全地执行解释器中的代码。

这意味着,如果您有一些相当耗费计算资源的线程,也就是说,在解释器中做了很多事情,那么您实际上仍然只有单线程程序的性能。在这种情况下,您最好使用multiprocessing模块,它与multithreading模块具有相同的接口,但会启动多个解释器副本(缺点是您必须明确共享内存)。

在Python中,如果您正在进行大量IO操作,仍然可以从多线程中获得速度提升。当一个线程等待磁盘或网络I/O时,其他线程仍然可以执行,因为当线程阻塞时,它们会释放解释器锁。


甚至比单线程程序更糟糕的性能:请观看Dave Beazley的这个精彩演讲,理解GIL,深入了解。 - Chris Wesseling

12

CPython使用引用计数和循环垃圾回收机制进行内存管理。为了使其实用化,它有一个称为“全局解释器锁”的机制来保护引用计数系统以及所有其他解释器内部。

在单核机器上,这并不重要——所有线程都是通过时间片轮转实现的。但在多核机器上,情况就不同了:在CPython上运行的CPU绑定型Python程序将无法利用所有可用的核心。

对此,有很多可能的解决办法:

  • 使用多个进程而非多个线程(还可以将未来的可扩展性扩展到多台机器,而非单台机器中的不同核心)
  • 使用具有更好的多核友好型垃圾回收机制的Python实现(如Jython、IronPython或PyPy)
  • 将更加密集的CPU操作移至C代码,并在执行计算时释放GIL(这样即使只有一个Python线程处于活动状态,也可以让C代码在其他核心上运行)

如果正在使用线程将阻塞IO转换为非阻塞操作,则在标准CPython中可以正常工作,无需任何特殊修改——IO操作已经释放了GIL。


7
您可以使用multiprocessing,来解决这个问题!在Python中,它与多线程一样简单,但可以充分利用CPU的所有核心。

multiprocessing是一个支持使用类似于线程模块的API来生成进程的包。multiprocessing包提供了本地和远程并发,通过使用子进程而不是线程有效地绕过全局解释器锁定。因此,multiprocessing模块允许程序员充分利用给定机器上的多个处理器。它可运行于Unix和Windows。


2

有一个选择是使用不同的Python实现,比如Jython或IronPython。这样,您仍然可以享受使用Python语言的好处,而不必处理GIL。但是,您将无法使用仅限于CPython的库。

另一个选择是使用不同的构造而不是线程。例如,如果您使用Stackless Python,Tasklets是一种替代方案。


1

在CPython中,线程是高效的,但线程不能在不同的处理器/核心上并发运行。这可能就是所说的。只有在需要进行共享内存并发时才会受到影响。

其他Python实现没有这个问题。


-1
在找到以下解决方案后,我想知道为什么Python仍然有线程类。
from multiprocessing import Pool

def some_function(x):
    return x*x

Xs = [i for i in xrange(1, 1000)]      # make 1, 2, ..., 999, 1000

pool = Pool(processes=16)              # start 16 worker processes on 16 CPU
print pool.map(some_function, Xs)      # print out 

1
因为Thread类和Pool类是用于两个不同的事情。 - Austin Henley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接