meh*_*hdy 3 python parallel-processing concurrency multithreading
我想知道 python 线程是并发运行还是并行运行?
例如,如果我有两个任务并在两个线程中运行它们,它们是同时运行还是计划并发运行?
我知道GIL并且线程只使用一个 CPU 内核。
这是一个需要大量解释的复杂问题。我将坚持使用 CPython,因为它是最广泛使用的并且我有经验。
Python 线程是一个系统线程,它需要 Python 解释器在运行时本地将其内容执行为字节码。GIL 是特定于解释器(在本例中为 CPython)的锁,它强制每个线程获取解释器上的锁,防止两个线程同时运行,无论它们在哪个内核上。
没有一个CPU 内核一次可以运行多个线程。您甚至需要多个内核才能明智地谈论并行性。并发性与并行性不同——前者意味着两个线程之间的操作可以在任何一个完成之前交错,但两个线程不需要同时启动,而后者意味着可以同时启动的操作。如果这让您感到困惑,这里有关于差异的更好描述。
有多种方法可以在单核 CPU 中引入并发性——即让线程挂起(让自己进入睡眠状态)并在需要时恢复——但没有办法在单核中引入并行性。
由于这些事实,因此,它取决于。
系统线程本质上被设计为并发的 - 否则拥有操作系统就没有多大意义。它们是否以这种方式实际执行取决于任务:某处是否有原子锁?(正如我们将看到的,有!)
执行受 CPU 限制的计算的线程——其中有大量代码正在执行,并且同时为每一行动态调用解释器——获得 GIL 上的锁,以防止其他线程执行相同的操作。因此,在这种情况下,一次只有一个线程在所有内核上工作,因为没有其他线程可以获取解释器。
话虽这么说,线程也不需要保持GIL,直到他们完成,而不是采集数据并在需要时为/解除锁定。这是可能的两个线程交错他们的行动,因为GIL可以在一个代码块,由其他线程抓住年底发布,在年底发布该代码块,等等。它们不会并行运行——但它们当然可以同时运行。
另一方面,I/O 绑定线程花费大量时间只是等待请求完成。这些线程不会获得 GIL - 当没有什么可以解释的时候,它们为什么会呢?- 所以当然你可以有多个 I/O 等待线程并行运行,每个线程一个核心。分钟代码需要编译为字节码,但是(也许您需要处理您的请求?)再次上升到 GIL。
Python 中的进程在 GIL 中幸存下来,因为它们是与线程捆绑在一起的资源集合。每个进程都有自己的解释器,因此进程中的每个线程只需与其直接进程的兄弟进程竞争 GIL。这就是为什么在 Python 中推荐使用基于进程的并行性的方法,即使它总体上消耗更多资源。
因此,两个线程中的两个任务可以并行运行,前提是它们不需要访问 CPython 解释器。如果它们正在等待 I/O 请求或使用不需要 Python 解释器的合适的其他语言(例如 C)扩展,使用外部函数接口,则可能发生这种情况。
在交错原子操作的意义上,所有线程都可以并发运行。这些交错究竟有多原子- 在代码块之后释放 GIL 吗?每行之后?- 取决于任务和线程。Python 线程不必串行执行——一个线程完成,然后另一个线程开始——所以在这个意义上存在并发。
归档时间: |
|
查看次数: |
1229 次 |
最近记录: |