我一直试图围绕线程如何在Python中运行,并且很难找到有关它们如何运行的良好信息.我可能只是错过了一个链接或东西,但似乎官方文档在这个主题上不是很彻底,我找不到一个好的写作.
据我所知,一次只能运行一个线程,活动线程每10个指令左右切换一次?
哪里有一个很好的解释,或者你可以提供一个?了解在使用Python线程时遇到的常见问题也是非常好的.
是的,因为全局解释器锁(GIL),一次只能运行一个线程.以下是一些关于此的一些见解的链接:
http://www.artima.com/weblogs/viewpost.jsp?thread=214235
http://smoothspan.wordpress.com/2007/09/14/guido-is-right-to-leave-the-gil-in-python-not-for-multicore-but-for-utility-computing/
从最后一个链接有趣的引用:
让我解释一下这意味着什么.线程在同一虚拟机内运行,因此在同一台物理机器上运行.进程可以在同一物理计算机或另一台物理计算机上运行.如果围绕线程构建应用程序,则无法访问多台计算机.因此,您可以扩展到单台计算机上的核心数量(随着时间的推移会有很多核心),但要真正达到网络规模,您无论如何都需要解决多机器问题.
如果要使用多核,则pyprocessing定义基于进程的API以进行实际并行化.该PEP还包括了一些有趣的基准.
Python是一种相当容易使用的语言,但有一些警告.您需要了解的最重要的事情是Global Interpreter Lock.这只允许一个线程访问解释器.这意味着两件事:1)你很少发现自己在python中使用了锁语句,2)如果你想利用多处理器系统,你必须使用单独的进程.编辑:我还应该指出,如果你想绕过GIL你可以把一些代码放在C/C++中.
因此,您需要重新考虑使用线程的原因.如果要并行化应用程序以利用双核架构,则需要考虑将应用程序分解为多个进程.
如果要提高响应能力,则应考虑使用线程.还有其他选择,即微线程.您还应该研究一些框架:
无堆栈的蟒蛇
greenlets
GEVENT
单片眼镜
下面是一个基本的线程示例.它会产生20个线程; 每个线程都会输出其线程号.运行它并观察它们的打印顺序.
import threading class Foo (threading.Thread): def __init__(self,x): self.__x = x threading.Thread.__init__(self) def run (self): print str(self.__x) for x in xrange(20): Foo(x).start()
正如您所暗示的那样,Python线程是通过时间切片实现的.这就是他们获得"平行"效果的方式.
在我的示例中,我的Foo类扩展了线程,然后我实现了该run
方法,这是您希望在线程中运行的代码所在的位置.要启动线程,请调用start()
线程对象,这将自动调用run
方法...
当然,这只是非常基础.您最终希望了解用于线程同步和消息传递的信号量,互斥锁和锁定.
如果各个worker正在进行I/O绑定操作,请在python中使用线程.如果你想扩展到多个内核的机器上找到一条好的IPC的Python框架或选择一个不同的语言.
注意: 无论我在何处提及,thread
我的意思是在明确声明之前专门在python中使用线程。
如果您来自C/C++
后台,线程在python中的工作方式会有所不同。在python中,给定时间只能有一个线程处于运行状态,这意味着python中的线程无法真正利用多个处理内核的功能,因为根据设计,线程不可能在多个内核上并行运行。
由于python中的内存管理不是线程安全的,因此每个线程都需要对python解释器中的数据结构进行独占访问。此独占访问是通过一种称为(全局解释器锁)的机制获得的。GIL
Why does python use GIL?
为了防止多个线程同时访问解释器状态并破坏解释器状态。
这个想法是,每当执行一个线程时(即使它是主线程),都会获取一个GIL,并且在某个预定义的时间间隔后,当前线程会释放GIL,而其他某个线程(如果有)会重新获取GIL。
Why not simply remove GIL?
删除GIL并不是不可能的,只是这样做的目的是,我们最终将多个锁放入解释器中以序列化访问,这甚至使单线程应用程序的性能降低。
因此删除GIL的成本是通过降低单线程应用程序的性能来弥补的,这是从未希望的。
So when does thread switching occurs in python?
GIL释放时发生线程切换。那么GIL何时释放?有两种情况需要考虑。
如果线程正在执行CPU绑定操作(Ex图像处理)。
在旧版本的python中,线程切换通常在固定数量的python指令之后发生,默认情况下设置为。100
由于执行一条指令所花费的时间,决定何时应该进行切换并不是一个很好的策略可能从毫秒到一秒甚至很疯狂。因此,每条100
指令之后释放GIL 而不管它们执行的时间是一个糟糕的策略。
在新版本中,不是使用指令计数作为切换线程的度量标准,而是使用了可配置的时间间隔。默认的切换间隔是5毫秒。您可以使用来获取当前的切换间隔sys.getswitchinterval()
。可以使用更改sys.setswitchinterval()
如果线程正在执行某些IO绑定操作(例如文件系统访问或
网络IO)
每当线程等待某些IO操作完成时,就会释放GIL。
Which thread to switch to next?
解释器没有自己的调度程序。在间隔结束时调度哪个线程是操作系统的决定。。