python线程是如何工作的?

python线程是如何工作的?,python,multithreading,concurrency,parallel-processing,Python,Multithreading,Concurrency,Parallel Processing,我想知道python线程是并发运行还是并行运行 例如,如果我有两个任务,并在两个线程内运行它们,它们是同时运行还是计划同时运行 我知道GIL和线程只使用一个CPU核。在CPython中,线程是真正的操作系统线程,并且被调度为由操作系统并发运行。但是,正如您所注意到的,GIL意味着一次只有一个线程执行指令 这是一个需要大量解释的复杂问题。我将坚持使用CPython,因为它是应用最广泛的,也是我有经验的 Python线程是一个系统线程,它要求Python解释器在运行时以本机方式将其内容执行为字节码

我想知道python线程是并发运行还是并行运行

例如,如果我有两个任务,并在两个线程内运行它们,它们是同时运行还是计划同时运行


我知道GIL和线程只使用一个CPU核。

在CPython中,线程是真正的操作系统线程,并且被调度为由操作系统并发运行。但是,正如您所注意到的,GIL意味着一次只有一个线程执行指令

这是一个需要大量解释的复杂问题。我将坚持使用CPython,因为它是应用最广泛的,也是我有经验的

  • Python线程是一个系统线程,它要求Python解释器在运行时以本机方式将其内容执行为字节码。GIL是一个特定于解释器的锁(在本例中为CPython),它强制每个线程获取解释器上的锁,从而防止两个线程在同一时间运行,无论它们在哪个内核上

  • 没有CPU核心一次可以运行多个线程。您甚至需要多个内核才能明智地谈论并行性。并发性与并行性不同——前者意味着两个线程之间的操作可以在其中一个线程完成之前进行交错,但两个线程都不需要同时启动,而后者则意味着可以同时启动操作。如果这让你感到困惑,最好对差异进行更好的描述

  • 有一些方法可以在单核CPU中引入并发性——即,让线程挂起(让它们自己进入睡眠状态)并在需要时恢复——但是没有办法在单核CPU中引入并行性

由于这些事实,因此,这要视情况而定

  • 系统线程本质上是设计为并发的——否则操作系统就没有多大意义了。它们是否真正以这种方式执行取决于任务:是否有原子锁?(正如我们将要看到的,有!)

  • 执行CPU限制计算的线程(其中有大量代码正在执行,同时为每行动态调用解释器)在GIL上获得一个锁,该锁阻止其他线程执行相同的计算。因此,在这种情况下,一次只能有一个线程跨所有内核工作,因为没有其他线程可以获取解释器

    也就是说,线程不需要保持GIL直到完成,而是在需要时获取并释放锁。两个线程可以交错它们的操作,因为GIL可以在代码块的末尾释放,被另一个线程捕获,在代码块的末尾释放,依此类推。它们不会并行运行,但它们肯定可以并发运行

  • 另一方面,与I/O绑定的线程只需等待请求完成,就会花费大量时间。这些线程没有获得GIL-为什么它们会在没有任何解释的情况下获得GIL因此,您当然可以让多个I/O等待线程并行运行,每个线程一个内核。然而,需要编译成字节码的分钟代码(也许你需要处理你的请求?)又上升了GIL

  • Python中的进程在GIL中幸存下来,因为它们是与线程捆绑在一起的资源集合。每个进程都有自己的解释器,因此进程中的每个线程只需与自己的直接进程同级竞争GIL。这就是为什么在Python中基于进程的并行性是推荐的方法,尽管它总体上消耗了更多的资源

其结果 因此,两个线程中的两个任务可以并行运行,前提是它们不需要访问CPython解释器。如果他们正在等待I/O请求,或者正在使用不需要Python解释器的合适的其他语言(比如C)扩展,使用外部函数接口,则可能发生这种情况


在交错原子操作的意义上,所有线程都可以并发运行。这些交错的原子性到底有多大——GIL是在代码块之后释放的吗?每行之后?-取决于任务和线程。Python线程不必串行执行—一个线程完成,然后另一个线程启动—因此在这个意义上存在并发性。

让我解释一下这一切意味着什么。线程在同一虚拟机内运行,因此在同一物理机上运行。进程可以在同一台物理机器上运行,也可以在另一台物理机器上运行。如果您围绕线程构建应用程序,那么您在访问多台计算机方面什么都没有做。因此,您可以扩展到单个机器上的任意多个内核(随着时间的推移,这将是相当多的),但要真正达到web规模,您仍然需要解决多个机器的问题