ruby有真正的多线程吗?

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

78932 次浏览

Ruby 1.8只有绿色线程,没有办法创建一个真正的“操作系统级”线程。但是,ruby 1.9将有一个叫做光纤的新特性,它将允许您创建实际的操作系统级线程。不幸的是,Ruby 1.9仍处于测试阶段,预计几个月后就会稳定下来。

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

使用drb怎么样?它不是真正的多线程,而是几个进程之间的通信,但你现在可以在1.8中使用它,而且它的阻力相当低。

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

如果你真的需要Ruby在产品级系统中的并行性(在那里你不能使用beta),进程可能是更好的选择。< br > 但是,首先在JRuby下尝试线程是非常值得的

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

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

你似乎混淆了两个非常不同的东西 Ruby编程语言和特定的线程模型之一 Ruby编程语言的具体实现。在那里 目前大约有11种不同的Ruby实现 编程语言,与非常不同且唯一的线程 模型。< / p >

(不幸的是,这11个实现中只有两个是真正的 准备投入生产使用,但要到年底这个数字 (更新:现在是5:MRI, JRuby, YARV (Ruby 1.9的解释器),Rubinius和IronRuby)。

    第一个实现实际上没有名称 这让我很尴尬,而且真的很烦人 让人困惑。它通常被称为“Ruby”,这是偶数 比没有名字更烦人更让人困惑,因为 导致了Ruby特性之间无休止的混乱

    .编程语言和特定的Ruby实现 它有时也被称为“MRI”(为“Matz的Ruby ), CRuby或MatzRuby.

    MRI实现Ruby线程为绿色线程 翻译< / >。不幸的是,它不允许这些线程 如果要并行调度,则一次只能运行一个线程 时间。< / p > 然而,任何数量的C线程(POSIX线程等)都可以运行 并行于Ruby线程,所以外部C库,或MRI 创建自己线程的C扩展仍然可以运行 李平行。< / p > < / > 第二个实现是YARV (Yet的缩写) 另一个Ruby VM”)。YARV实现Ruby线程为POSIX或 Windows NT Threads,但是,它使用全局解释器 锁定(GIL),以确保实际上只有一个Ruby线程

    .

    .

    像MRI一样,C线程可以实际上与Ruby线程并行运行。

    在未来,有可能GIL 可能被破坏 到更细粒度的锁,从而允许越来越多的 代码实际上是并行运行的,但那太遥远了,它是 not even 计划 yet.

    <李> < p > JRuby 将Ruby线程实现为原生线程, 其中“本机线程”在JVM的情况下显然意味着“JVM ? 线程”。JRuby没有对它们施加额外的锁定。所以, 这些线程是否可以并行运行取决于 JVM:一些JVM实现JVM线程作为操作系统线程和一些 叫做绿线。(Sun/Oracle的主流jvm从JDK 1.3开始只使用OS线程)

  1. XRuby实现Ruby线程作为JVM线程Update: XRuby已死。

  2. <李> < p > IronRuby 将Ruby线程实现为原生线程, 在CLR的情况下,“本机线程”明显意味着什么 “CLR线程”。IronRuby没有对它们施加额外的锁定, 所以,它们应该并行运行,只要你的CLR支持 李。< / p > < / >
  3. Ruby。网 also 实现Ruby线程作为CLR 线程< / >。更新: Ruby。

  4. . NET已死
  5. Rubinius 实现Ruby线程为绿色线程 在它的虚拟机。更准确地说:Rubinius VM导出非常轻量级,非常灵活 并发/并行/非本地控制流构造,称为 一个“任务”,以及所有其他并发构造(线程在 这个讨论,还有延续演员和 其他东西)在纯Ruby中实现,使用Tasks

    Rubinius不能(目前)并行调度线程, 不过,他补充说,这不是太大的问题:Rubinius可以 already 在多个POSIX线程中运行多个VM实例 parallel,在一个Rubinius进程内。因为线程是 实际上是用Ruby实现的,就像其他Ruby一样 对象,被序列化并发送到不同的虚拟机 POSIX线程。(这与BEAM Erlang VM的模型相同 用于SMP并发。它已经是实现 Rubinius演员< / >)。< / p >

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

  6. MacRuby开始时是YARV在 Objective-C运行时,CoreFoundation和Cocoa框架。它 现在已经与YARV有了很大的分歧,但是现在呢 仍然与YARV共享相同的线程模型更新: MacRuby依赖于apple垃圾回收器,该垃圾回收器已被声明为弃用,将在MacOSX的后续版本中被移除,MacRuby是不死的

  7. 红衣主教是一个Ruby实现鹦鹉 线程< / >。更新: Cardinal seems very inactive/dead.

  8. 磁悬浮宝石石/S的Ruby实现 Smalltalk VM < / >。我不知道什么线程模型 GemStone/S使用什么线程模型,MagLev使用什么线程模型 线程甚至还没有实现(可能没有)。

  9. 如果是它的一个完整的Ruby实现 自己的。它是YARV字节码虚拟机的实现 JavaScript。HotRuby不支持线程(还没有? ,它们将无法并行运行,因为JavaScript 不支持真正的并行性。有一个ActionScript 然而,HotRuby的版本和ActionScript可能实际上 支持并行性。更新: HotRuby is dead.

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

所以,如果你想要真正的并行线程,JRuby目前是你的 唯一的选择——并不是说这是一个坏的选择:JRuby实际上更快

否则,“经典的”Ruby解决方案是使用进程 而不是并行的线程。Ruby核心库 包含Process.forkProcess模块 方法,这使得它非常容易分叉另一个Ruby 的过程。此外,Ruby标准库包含 分布式Ruby (dRuby / dRb)库,它允许Ruby 代码应该简单地分布在多个进程中,而不是

.

.

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

它取决于实现:

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

< br >

< p > < br > < br > Ruby将闭包定义为BlockslambdasProcs。为了充分利用JRuby中的闭包和多核,Java的执行人派上用场;对于MacRuby,我喜欢肾小球囊性肾病的队列。 < br > < br > 注意,能够创建真正的“操作系统级”线程并不意味着你可以使用多个cpu内核进行并行处理。请看下面的例子

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

(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线程实现方式的限制。


< p > < br > 同样的程序,现在是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

< p > < br > 同样的程序,现在有了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

< p > < br > 同样的程序,但是现在用的是老式的核磁共振成像。由于这个实现使用绿色线程,所以只有一个线程显示

(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
< p > < br > < br > 如果你对Ruby多线程感兴趣,你可能会发现我的报告使用fork处理程序调试并行程序很有趣 对于Ruby内部的更全面的概述,显微镜下的Ruby 是一个很好的阅读。< br > 此外,Omniref中的Ruby线程和全局解释器锁定在C语言中在源代码中解释了为什么Ruby线程不能并行运行

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

jruby 1.5.6 (ruby 1.8.7 patchlevel 249) (2014-02-03 6586) (OpenJDK 64-Bit Server VM 1.7.0_75) [amd64-java]

. txt

第二种是:

Ruby 2.2.1.p95 (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

因为不能编辑那个答案,所以在这里添加一个新的回答。

更新(2017-05-08)

这篇文章很旧了,信息也跟不上时代 (2017)胎面,以下是一些补充:

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

  2. truffleruby是Ruby编程语言的高性能实现。TruffleRuby是由Oracle实验室在GraalVM上构建的,它是JRuby的一个分支,结合了来自Rubinius项目的代码,也包含了来自Ruby的标准实现的代码,MRI,仍然处于开发阶段,还没有准备好生产。 这个版本的ruby似乎是为性能而生的,我不知道是否支持并行线程,但我认为它应该支持