Python、Ruby、Haskell - 它们提供真正的多线程吗?

16
我们计划使用任何一种高级编程语言编写高度并发的应用程序。
1)Python、Ruby 或 Haskell 支持真正的多线程吗?
2)如果程序包含线程,虚拟机是否会自动将工作分配给多个核心(或者如果主板上有多个 CPU,则分配到物理 CPU 上)?
"真正的多线程" 指的是:多个独立的执行线程利用多个核心提供的资源(而不仅仅是一个核心)。
"伪多线程" 是指:线程模拟多线程环境,而无需依赖任何本地操作系统功能。

5
"真正的"多线程?这是什么意思?请定义 "真正的"多线程。 真正的多线程是指在一个程序中同时执行多个线程,每个线程都可以独立地运行,而不需要依赖于其他线程或进程。这种方法可以提高程序的效率和性能,并且可以更好地利用多核处理器的优势。 - S.Lott
3
真正的多线程 = 多个独立执行线程利用多个核心提供的资源(而不是仅由一个核心提供)。 - psihodelia
@S.Lott:完成了,我已经更新了原始帖子。 - psihodelia
5
现在你更新了问题,你对“真”和“假”多线程的区分让我更加困惑了。例如,BEAM Erlang VM可以跨多个CPU和多个核调度Erlang线程。因此,根据您的定义,BEAM支持真正的多线程。但是,BEAM不以任何方式依赖本地操作系统功能;事实上,它甚至可以在裸机上完全运行而无需任何操作系统。因此,根据您的定义,BEAM并不支持真正的多线程,而是具有虚假的多线程。这是什么意思?那个定义是毫无用处的。 - Jörg W Mittag
5
BEAM之所以如此可扩展,完全是因为它不依赖于笨重、臃肿、缓慢的操作系统线程,而是实现了自己的线程。例如,在32位机器上,Linux线程大小为4或8 KiB,64位机器上为8或16 KiB。我认为Windows NT线程大小为12 KiB。BEAM的线程大小约为300字节。32位Linux可以轻松处理成千上万个线程。在至少32位系统上,大约需要80万个线程才能耗尽内存。我曾经看到BEAM在一台性能不太强的netbook上运行演示时,处理100万个线程。 - Jörg W Mittag
8个回答

34

1) Python、Ruby或Haskell是否支持真正的多线程?

这与编程语言本身无关,而是与硬件有关(如果计算机只有一个 CPU,则同时执行两条指令在物理上是不可能的)、操作系统有关(同样地,如果操作系统不支持真正的多线程,则你什么也做不了)以及语言实现/执行引擎有关。

除非编程语言明确禁止或强制使用真正的多线程,否则这与编程语言本身完全没有关系。

你提到的所有编程语言,以及之前答案中提到的所有编程语言,都有多个实现版本,其中一些支持真正的多线程,一些不支持,并且一些是建立在其他执行引擎之上,这些引擎可能支持或不支持真正的多线程。

以 Ruby 为例,以下是它的一些实现版本和它们的线程模型:

  • MRI:绿色线程,不支持真正的多线程
  • YARV:OS 线程,不支持真正的多线程
  • Rubinius:OS 线程,支持真正的多线程
  • MacRuby:OS 线程,支持真正的多线程
  • JRuby、XRuby:JVM 线程,取决于 JVM(如果 JVM 支持真正的多线程,则 JRuby/XRuby 也会支持;如果 JVM 不支持,则它们无能为力)
  • IronRuby、Ruby.NET:与 JRuby、XRuby 类似,但在 CLI 上而不是在 JVM 上

另请参阅我对Ruby的另一个相似问题的回答。(请注意,那个答案已经超过一年了,其中有些内容已经不再准确。例如,现在Rubinius使用真正的并发本地线程,而不是真正的并发绿色线程。此外,自那时以来,出现了几个新的Ruby实现,如BlueRuby、tinyrb、Ruby Go Lightly、Red Sun和SmallRuby。)

对于Python也是类似情况:

  • CPython:原生线程,无真正的多线程
  • PyPy:原生线程,取决于执行引擎(PyPy可以本地运行,也可以运行在JVM、CLI之上,或者运行在另一个Python执行引擎之上。只要底层平台支持真正的多线程,PyPy也支持)
  • Unladen Swallow:原生线程,目前没有真正的多线程,但计划进行修复
  • Jython:JVM线程,参见JRuby
  • IronPython:CLI线程,参见IronRuby

对于Haskell,至少GHC(Glorious Glasgow Haskell Compiler)支持使用本地线程实现真正的多线程。我不知道UHC、LHC、JHC、YHC、HUGS或其他所有实现的情况。

对于Erlang,BEAMHiPE都支持使用绿色线程实现真正的多线程。

2)如果程序包含线程,虚拟机是否会自动分配任务到多个核心(或者如果主板上有多个CPU,则分配到物理CPU)?

同样地:这取决于虚拟机、操作系统和硬件。此外,上述一些实现甚至没有虚拟机


2
CPython和YARV即使在多核处理器上运行,甚至使用本地线程,也没有真正的多线程,这是因为它们有一个全局解释器锁,以防止线程在其他线程尝试更改执行环境(例如可用类定义和方法调用集)时看到不一致状态。 - Ken Bloom
2
YARV 称之为 Giant VM Lock (GVL),但原则上你是正确的。至少在 YARV 上,有计划移除 GIL。如果你查看 thread.c,你可以看到 YARV 经历或将经历的三种不同线程模型的描述:绿色线程 -> 带 GIL 的本地线程 -> 真正的并行本地线程,带有细粒度锁定。然而,目前还没有代码。在 CPython 中,情况类似:Unladen Swallow 计划移除 GIL,并且如果不会太过侵入性,将这个移除贡献回 CPython。 - Jörg W Mittag

22

Haskell实现GHC支持在共享内存多核上并行执行的多种机制。这些机制在“《为多核Haskell提供运行时支持》”中进行了描述。

具体而言,Haskell运行时将工作划分为N个操作系统线程,并分布在可用的计算核心上。这些N个操作系统线程又运行M个轻量级的Haskell线程(有时会有数百万个线程)。反过来,每个Haskell线程都可以从一个火花队列中获取工作(可能有数十亿个火花)。就像下面这样: enter image description here

运行时安排工作在不同的核心上执行,迁移工作并负载平衡。垃圾收集器也是并行的,使用每个核心来收集堆的一部分。

与Python或Ruby不同,Haskell没有全局解释器锁,因此出于这个原因等等,与其他语言相比,在多核上,例如Haskell和Python在多核 shootout 中的比较,GHC表现尤为出色。


16

如果你使用-threaded选项进行编译,并在运行时传递+RTS -N<x> -RTS,其中<x>表示你想要的操作系统线程数,那么GHC编译器将在多个操作系统线程(因此多个内核)上运行你的程序。


1
或者如果你省略了 <x>,它会尝试找出最优的数字。 - Palmik

7

目前的 Ruby 1.9 版本(基于 C 的 YARV 版本)具有原生线程,但存在 GIL 问题。据我所知,Python 也存在 GIL 问题。

然而,Jython 和 JRuby(Ruby 和 Python 的成熟 Java 实现)都提供原生多线程,没有绿色线程,也没有 GIL。

不清楚 Haskell 的情况。


Jython和JRuby与它们的起源非常不同吗? - psihodelia
GIL 是什么意思?谷歌建议是“气体绝缘线路”,这并没有什么帮助... - tobsen
2
http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock 解释了什么是全局解释器锁(GIL)。 - tobsen
@psihodelia 不,它们并没有区别,只是使用Java而不是C编写。如果您有一些C扩展,则无法在Jython和JRuby内部使用它们,但实际上,Jython和JRuby都允许您在代码中使用Java jars,这是一个很大的替代方案和优势,我可以告诉您。 - khelll
@tobsen,GIL代表全局解释器锁。 - khelll

1

Haskell是支持线程的,此外它还是一种纯粹的函数式语言 - 没有副作用


那又怎样?纯净的代码使得线程处理变得容易,因为你知道函数可以在其他线程中运行而不会修改环境——Haskell也有Monad,它们是代码的非纯部分,但这并没有回答OP的问题。请删除此回答。 - Evan Carroll

1

如果你想要真正的并发性,你可能想尝试一下 Erlang。


看起来Erlang也不支持真正的多线程。根据维基百科:进程是构建Erlang应用程序的主要手段。Erlang进程既不是操作系统进程也不是操作系统线程,而是类似于Java原始“绿色线程”的轻量级进程。 - psihodelia
1
那个维基百科的引用与真正的多线程有什么关系? - Jörg W Mittag
3
Erlang在分布式方面表现出色,并具有良好的并发抽象,但是并发不等于并行,并且与Haskell相比,Erlang的多核运行时较新且略慢。尽管如此,与Python或Ruby相比要好得多,您将学习很多关于并发编程的知识,只是不包括多核并行编程。 - Don Stewart

1
我赞同选择Erlang。Erlang可以支持开箱即用的分布式高并发编程。无论你是称其为“多线程”还是“多进程”,都不重要。需要考虑的两个重要因素是并发级别和Erlang进程不共享状态。
进程之间没有共享状态是一件好事。

-2

Haskell 可以用于任何事情。 Python 有 processing 模块,它(我认为 - 不确定)有助于避免 GIL 问题。(因此它也适用于任何事情)。

但是我的观点是,对于大型和巨大的项目,最好选择具有静态类型系统的最高级别语言。今天这些语言是:ocaml、haskell、erlang。

如果你想开发小东西 - Python 是不错的选择。但当事情变得更加复杂时 - 所有 Python 的优点都被无数的测试所消耗。

我没有使用过 Ruby。我仍然认为 Ruby 是一种玩具语言。(或者至少在你知道 Python 的情况下,没有理由去学习 Ruby - 最好读 SICP 书)。


嗯...我的错,那句话不太好。我最好在另外一句话中谈论Erlang。 - Vasiliy Stavenko

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