我知道 ruby​​ 使用的“协作”线程 绿线. 。如何在应用程序中创建真正的“操作系统级”线程,以便利用多个 cpu 核心进行处理?

有帮助吗?

解决方案

更新了 Jörg 2011 年 9 月的评论

你似乎混淆了两个 非常 这里有不同的东西:这 Ruby编程语言及其具体线程模型之一 Ruby 编程语言的具体实现。那里 目前大约有 11 种不同的 Ruby 实现 编程语言,与 非常 不同且独特的线程 模型。

(不幸的是,这 11 个实现中只有两个实际上是 准备投入生产使用,但到今年年底,这个数字 可能会上升到四五个。(更新: :现在是 5:MRI、JRuby、YARV(Ruby 1.9 的解释器)、Rubinius 和 IronRuby)。

  1. 第一个实现实际上没有名称,它 提到它很尴尬,真的很烦人,而且 混乱。它通常被称为“Ruby”,它甚至 比没有名字更烦人和困惑,因为它 导致 Ruby 功能之间无休止的混淆 编程语言和特定的 Ruby 实现。

    它有时也被称为“MRI”(用于“Matz's Ruby” 实现“)、CRuby 或 MatzRuby。

    MRI将Ruby线程作为其解释器中的绿色线. 。不幸的是,它不允许这些线程 要并行调度,它们只能在 时间。

    但是,可以运行任意数量的 C 线程(POSIX 线程等) 与 Ruby 线程并行,因此外部 C 库或 MRI 创建自己的线程的 C 扩展仍然可以在 平行。

  2. 第二个实现是 亚病毒 (“然而”的缩写 另一个 Ruby VM“)。 yarv将红宝石线程用作POSIX或Windows NT线程, ,但是,它使用全局解释器 锁定 (GIL) 以确保实际上只有一个 Ruby 线程 安排在任何时间。

    像 MRI、C 线程 实际上与 Ruby 线程并行运行。

    未来,GIL 有可能 可能 坏了 向下到更细粒度的锁,从而允许越来越多的 代码实际并行运行,但这太遥远了,它是 甚至没有 计划 然而。

  3. JRuby 将 Ruby 线程实现为本机线程, 其中 JVM 中的“Native Threads”显然意味着“JVM 线程”。JRuby 不对它们强加额外的锁定。所以 这些线程是否真的可以并行运行取决于 JVM:一些 JVM 将 JVM 线程实现为操作系统线程,而一些 JVM 将 JVM 实现为 OS 线程 作为 Green Threads。(Sun/Oracle 的主流 JVM 自 JDK 1.3 起仅使用操作系统线程)

  4. 红宝石将 Ruby 线程实现为 JVM 线程. 更新: :XRuby 死了。

  5. 铁红宝石 将 Ruby 线程实现为本机线程, 其中 CLR 中的“本机线程”显然意味着 “CLR 线程”。IronRuby 没有对它们施加额外的锁定, 因此,只要 CLR 支持,它们就应该并行运行 那。

  6. Ruby.NET将红宝石线程作为CLR线程. 更新: Ruby.NET 已死。

  7. 鲁比纽斯 将红宝石线作为绿色线程在其虚拟机中. 。更确切地说:鲁比尼乌斯 虚拟机导出非常轻量级,非常灵活 并发/并行/非本地控制流构造,称为 a "任务“,以及所有其他并发构造(线程 这次讨论,也 延续, 演员 和 其他东西)是用纯 Ruby 实现的,使用 Tasks。

    Rubinius 不能(目前)并行调度线程, 但是,添加这并不是一个太大的问题:鲁比尼乌斯可以 已经 并行在几个POSIX线程中运行多个VM实例, ,在一个 Rubinius 过程中。由于线程是 实际上,它们可以像其他 Ruby 一样在 Ruby 中实现 对象,被序列化并发送到不同 POSIX 线程。(与 BEAM 相同型号 埃尔兰 虚拟机 用于 SMP 并发。已经是 为鲁比尼斯演员实施.)

    更新: :此答案中有关 Rubinius 的信息是关于 Shotgun VM 的,该虚拟机已不存在。“新”C++ VM 不使用跨多个 VM 调度的绿色线程(即Erlang/BEAM 风格),它使用更传统的单个 VM 和多个本机操作系统线程模型,就像 CLR、Mono 和几乎所有 JVM 所使用的模型一样。

  8. 麦克鲁比 最初是 YARV 在 Objective-C 运行时以及 CoreFoundation 和 Cocoa 框架。它 现在已经与 YARV 显着背道而驰,但 AFAIK 它目前 还 与 YARV 共享相同的线程模型. 更新: MacRuby 依赖于苹果垃圾收集器,该垃圾收集器已被声明弃用,并将在 MacOSX 的后续版本中删除,MacRuby 是不死的。

  9. 红衣主教 是一个 Ruby 实现 鹦鹉虚拟机. 。然而,它还没有实现线程, 当它这样做时,它可能会将它们实现为 鹦鹉线. 更新: :红衣主教看起来非常不活跃/死气沉沉。

  10. 磁浮 是一个 Ruby 实现 宝石/S Smalltalk VM. 。我不知道什么线程模型 GemStone/S 使用,MagLev 使用什么螺纹模型,甚至 线程甚至还没有实现(可能还没有)。

  11. 热红宝石不是 完整的 Ruby 实现 有。它是 YARV 字节码 VM 的实现 JavaScript的。HotRuby还不支持线程,以及何时支持 ,它们将无法并行运行,因为 JavaScript 不支持真正的并行性。有一个 ActionScript 然而,HotRuby 的版本和 ActionScript 实际上可能 支持并行性。 更新: :热鲁比死了。

不幸的是,这 11 个 Ruby 实现中只有两个是 实际生产就绪:MRI 和 JRuby。

所以,如果你想要真正的并行线程,JRuby目前是你的 唯一的选择——并不是说这是一个糟糕的选择:JRuby实际上更快 比 MRI 更稳定,可以说更稳定。

否则,“经典”的 Ruby 解决方案是使用进程 而不是用于并行性的线程。Ruby 核心库 包含 Process 模块Process.fork 方法 这使得分叉另一个 Ruby 变得非常容易 过程。此外,Ruby 标准库还包含分布式 Ruby (dRuby / dRb) 库,允许 Ruby 代码将简单地分布在多个进程中,而不是 仅在同一台机器上,但也跨网络。

其他提示

Ruby 1.8 只有绿色线程,无法创建真正的“操作系统级”线程。但是,Ruby 1.9 将有一个称为 Fiber 的新功能,它允许您创建实际的操作系统级线程。不幸的是,Ruby 1.9 仍处于测试阶段,预计几个月后就会稳定下来。

另一种选择是使用 JRuby。JRuby 将线程实现为操作系统级主题,其中没有“绿色线程”。JRuby最新版本是1.1.4,相当于Ruby 1.8

这取决于实施:

  • MRI没有,YARV更接近。
  • JRuby 和 MacRuby 有。




红宝石有 关闭 作为 Blocks, lambdasProcs. 。为了充分利用 JRuby 中的闭包和多核, Java的执行者 派上用场;对于 MacRuby 我喜欢 GCD 的队列.

请注意,能够创建 真正的“操作系统级”线程 并不意味着您可以使用多个 cpu 核心进行并行处理。看下面的例子。

这是的输出 一个使用 3 个线程的简单 Ruby 程序 使用 Ruby 2.1.0:

(jalcazar@mac ~)$ ps -M 69877
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 69877 s002    0.0 S    31T   0:00.01   0:00.04 /Users/jalcazar/.rvm/rubies/ruby-2.1.0/bin/ruby threads.rb
   69877         0.0 S    31T   0:00.01   0:00.00 
   69877        33.4 S    31T   0:00.01   0:08.73 
   69877        43.1 S    31T   0:00.01   0:08.73 
   69877        22.8 R    31T   0:00.01   0:08.65 

正如您在这里所看到的,有四个操作系统线程,但只有一个具有状态 R 在跑。这是由于 Ruby 线程的实现方式存在限制。



相同的程序,现在使用 JRuby。你可以看到三个线程的状态 R, ,这意味着它们是并行运行的。

(jalcazar@mac ~)$ ps -M 72286
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 72286 s002    0.0 S    31T   0:00.01   0:00.01 /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/bin/java -Djdk.home= -Djruby.home=/Users/jalcazar/.rvm/rubies/jruby-1.7.10 -Djruby.script=jruby -Djruby.shell=/bin/sh -Djffi.boot.library.path=/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni/Darwin -Xss2048k -Dsun.java.command=org.jruby.Main -cp  -Xbootclasspath/a:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jruby.jar -Xmx1924M -XX:PermSize=992m -Dfile.encoding=UTF-8 org/jruby/Main threads.rb
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    33T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.09   0:02.34 
   72286         7.9 S    31T   0:00.15   0:04.63 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.04   0:01.68 
   72286         0.0 S    31T   0:00.03   0:01.54 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.01   0:00.01 
   72286         0.0 S    31T   0:00.00   0:00.01 
   72286         0.0 S    31T   0:00.00   0:00.03 
   72286        74.2 R    31T   0:09.21   0:37.73 
   72286        72.4 R    31T   0:09.24   0:37.71 
   72286        74.7 R    31T   0:09.24   0:37.80 


同样的程序,现在使用 MacRuby。还有三个线程并行运行。这是因为 MacRuby 线程是 POSIX 线程 (真正的“操作系统级”线程)并且有 无GVL

(jalcazar@mac ~)$ ps -M 38293
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 38293 s002    0.0 R     0T   0:00.02   0:00.10 /Users/jalcazar/.rvm/rubies/macruby-0.12/usr/bin/macruby threads.rb
   38293         0.0 S    33T   0:00.00   0:00.00 
   38293       100.0 R    31T   0:00.04   0:21.92 
   38293       100.0 R    31T   0:00.04   0:21.95 
   38293       100.0 R    31T   0:00.04   0:21.99 


再次执行相同的程序,但现在使用的是老式 MRI。由于此实现使用绿色线程,因此仅显示一个线程

(jalcazar@mac ~)$ ps -M 70032
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 70032 s002  100.0 R    31T   0:00.08   0:26.62 /Users/jalcazar/.rvm/rubies/ruby-1.8.7-p374/bin/ruby threads.rb



如果您对 Ruby 多线程感兴趣,您可能会找到我的报告 使用 fork 处理程序调试并行程序 有趣的。
有关 Ruby 内部结构的更全面的概述 显微镜下的红宝石 是一本好书。
还, Ruby 线程和 C 中的全局解释器锁 Omniref 在源代码中解释了为什么 Ruby 线程不并行运行。

怎么样使用 德布?它不是真正的多线程,而是多个进程之间的通信,但您现在可以在 1.8 中使用它,而且它的摩擦相当低。

这个问题就让“系统监视器”来回答吧。在这两种情况下,我都使用在 i7(4 个超线程核心)机器上运行的 8 个 Ruby 线程执行相同的代码(如下,计算素数)...第一次运行是:

JRuby 1.5.6 (Ruby 1.8.7 补丁级别 249) (2014-02-03 6586) (OpenJDK 64 位服务器 VM 1.7.0_75)[amd64-java]

第二个是:

红宝石 2.1.2p95 (2014-05-08) [x86_64-linux-gnu]

有趣的是,JRuby 线程的 CPU 较高,但解释的 Ruby 的完成时间稍短。从图中很难看出,但第二次(解释的 Ruby)运行使用了大约 1/2 的 CPU(没有超线程?)

enter image description here

def eratosthenes(n)
  nums = [nil, nil, *2..n]
  (2..Math.sqrt(n)).each do |i|
    (i**2..n).step(i){|m| nums[m] = nil}  if nums[i]
  end
  nums.compact
end

MAX_PRIME=10000000
THREADS=8
threads = []

1.upto(THREADS) do |num|
  puts "Starting thread #{num}"
  threads[num]=Thread.new { eratosthenes MAX_PRIME }
end

1.upto(THREADS) do |num|
    threads[num].join
end

如果您使用 MRI,那么您可以用 C 语言编写线程代码作为扩展或使用 ruby​​-inline gem。

如果您确实需要 Ruby 中的并行性来实现生产级系统(您无法使用 Beta 版),那么流程可能是更好的选择。
但是,绝对值得首先尝试 JRuby 下的线程。

另外,如果您对 Ruby 下线程的未来感兴趣,您可能会发现这个 文章 有用。

以下是有关 Rinda 的一些信息,它是 Linda 的 Ruby 实现(并行处理和分布式计算范例) http://charmalloc.blogspot.com/2009/12/linda-tuples-rinda-drb-parallel.html

由于无法编辑该答案,因此在此处添加新回复。

更新(2017-05-08)

这篇文章很旧,信息不是最新的 (2017)胎面,以下是一些补充:

  1. 蛋白石 是一个 Ruby 到 JavaScript 源到源编译器。它还有一个 Ruby corelib 的实现,它目前的开发非常活跃,并且存在大量的(前端)框架。并准备生产。由于基于javascript,它不支持并行线程。

  2. 松露红宝石色 是 Ruby 编程语言的高性能实现。TruffleRuby 由 Oracle 实验室基于 GraalVM 构建,是 JRuby 的一个分支,将其与 Rubinius 项目的代码相结合,还包含来自 Ruby、MRI 标准实现的代码,仍处于实时开发状态,尚未准备好生产。这个版本的ruby似乎是为性能而生的,我不知道是否支持并行线程,但我认为应该。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top