我知道使用绿色线程的"红宝石""合作"线程。 如何在应用程序中创建真正的" OS级"线程,以便利用多个cpu内核进行处理?
更新为J?rg 2011年9月的评论
好。
您似乎在这里混淆了两个截然不同的事情:
Ruby编程语言和特定的线程模型之一
Ruby编程语言的特定实现。那里
目前大约有11种不同的Ruby实现
编程语言,具有非常不同和独特的线程
楷模。
好。
(不幸的是,这11个实现中只有两个是
准备用于生产,但到今年年底该数字
可能会增加到四到五个。)(更新:现在是5:MRI,JRuby,YARV(Ruby 1.9的解释器),Rubinius和IronRuby。
好。
第一个实现实际上没有名称,
使其引用起来很尴尬,而且确实很烦人,
令人困惑。它最常被称为" Ruby",甚至
比没有名字更烦人和混乱,因为它
导致Ruby功能之间无尽的混乱
编程语言和特定的Ruby实现。
好。
有时也称为" MRI"(对于" Matz's Ruby
实现"),CRuby或MatzRuby。
好。
MRI在其内部将Ruby线程实现为绿色线程
口译员。不幸的是,它不允许那些线程
要并行调度,它们只能在一个线程上运行一个线程
时间。
好。
但是,可以运行任何数量的C线程(POSIX线程等)。
与Ruby Thread并行,因此外部C库或MRI
创建自己的线程的C扩展仍可以在其中运行
平行。
好。
第二种实现是YARV("还"的缩写
另一个Ruby VM")。YARV将Ruby线程实现为POSIX或
Windows NT线程,但是,它使用全局解释器
锁定(GIL)以确保实际上只能有一个Ruby Thread
计划在任何时间。
好。
像MRI一样,C线程实际上可以与Ruby线程并行运行。
好。
将来,GIL可能会损坏
进入更细粒度的锁,从而允许越来越多
代码实际上可以并行运行,但是距离很远,
甚至还没有计划
好。
JRuby将Ruby线程实现为本机线程,
其中,对于JVM,"本地线程"显然意味着" JVM
线程"。JRuby不会对其施加任何额外的锁定。因此,
这些线程是否可以并行运行取决于
JVM:一些JVM将JVM线程实现为OS线程,而另一些
作为绿线。 (从JDK 1.3开始,Sun / Oracle的主流JVM仅使用OS线程)
好。
XRuby还将Ruby Threads实现为JVM Threads。更新:XRuby已死。
好。
IronRuby将Ruby线程实现为本地线程,
如果是CLR,"本地线程"显然意味着
" CLR线程"。 IronRuby没有对它们施加任何额外的锁定,
因此,只要您的CLR支持,它们就应该并行运行
那。
好。
Ruby.NET也将Ruby线程实现为CLR
线程。更新:Ruby.NET已死。
好。
Rubinius将Ruby线程实现为绿色线程
在其虚拟机中。更准确地说:Rubinius
VM导出非常轻巧,非常灵活
并发/并行/非本地控制流构造,称为
一个"任务",以及所有其他并发构造(
这个讨论,还有Continuations,Actors和
其他东西)是使用Tasks在纯Ruby中实现的。
好。
Rubinius无法(当前)并行调度线程,
但是,添加这个问题并不是太大:Rubinius可以
已经在多个POSIX线程中运行了多个VM实例
在一个Rubinius过程中并行进行。由于线程是
实际上是在Ruby中实现的,它们可以像其他任何Ruby一样
对象,进行序列化并发送到不同虚拟机中的其他虚拟机
POSIX线程。 (与BEAM Erlang VM的模型相同
用于SMP并发。已经针对
Rubinius演员。)
好。
更新:此答案中有关Rubinius的信息是关于Shotgun VM的,它不再存在。"新" C ++ VM不使用跨多个VM调度的绿色线程(即Erlang / BEAM样式),它使用具有多个本机OS线程模型的更传统的单个VM,就像CLR,Mono所采用的那样,几乎每个JVM。
好。
MacRuby最初是YARV的端口,
Objective-C运行时以及CoreFoundation和Cocoa框架。它
现在与YARV有很大的不同,但AFAIK目前
仍然与YARV共享相同的线程模型。
更新:MacRuby依赖于已宣布弃用的苹果垃圾收集器,并将在更高版本的MacOSX中将其删除,因此MacRuby是不死的。
好。
Cardinal是鹦鹉的Ruby实现
虚拟机。它还没有实现线程,但是,
当它这样做时,可能会将它们实现为Parrot
线程。更新:红衣主教似乎非常不活跃/死了。
好。
MagLev是GemStone / S的Ruby实现
Smalltalk VM。我不知道什么线程模型
GemStone / S使用MagLev使用什么线程模型,甚至
线程甚至尚未实现(可能没有实现)。
好。
HotRuby不是其完整的Ruby实现
拥有。它是YARV字节码VM的实现
JavaScript。 HotRuby不支持线程(还吗?)以及何时支持。
这样做,它们将无法并行运行,因为JavaScript
不支持真正的并行性。有一个动作脚本
版本,但是ActionScript实际上可能
支持并行性。更新:HotRuby已死。
好。
不幸的是,这11个Ruby实现中只有两个是
实际上已经可以投入生产:MRI和JRuby。
好。
因此,如果您想要真正的并行线程,那么JRuby当前是您的
唯一的选择–不是那不是一个坏选择:JRuby实际上更快
比MRI更可靠。
好。
否则,"经典" Ruby解决方案是使用进程
而不是并行线程。 Ruby核心库
包含Process模块和Process.fork
方法,使得分叉另一个Ruby很容易
处理。 此外,Ruby标准库还包含
分布式Ruby(dRuby / dRb)库,该库允许使用Ruby
将代码分散在多个进程中,而不是
只能在同一台计算机上,也可以跨网络。
好。
好。
Ruby 1.8仅具有绿色线程,无法创建真正的" OS级"线??程。但是,ruby 1.9将具有称为纤维的新功能,该功能将允许您创建实际的OS级线程。不幸的是,Ruby 1.9仍处于beta中,计划在几个月内保持稳定。
另一种选择是使用JRuby。 JRuby将线程作为操作系统级别的主题实现,其中没有"绿色线程"。 JRuby的最新版本是1.1.4,并且等效于Ruby 1.8
这取决于实现:
-
MRI没有,YARV更近了。
-
JRuby和MacRuby都有。
Ruby的闭包为Blocks,lambdas和Procs。为了充分利用JRuby中的闭包和多个内核,Java的执行器派上了用场。对于MacRuby,我喜欢GCD的队列。
请注意,能够创建真正的" OS级"线??程并不意味着您可以使用多个cpu内核进行并行处理。看下面的例子。
这是一个简单的Ruby程序的输出,该程序在Ruby 2.1.0中使用3个线程:
1 2 3 4 5 6 7
| (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 |
如您所见,有四个OS线程,但是只有状态为R的一个正在运行。这是由于Ruby线程的实现方式受到限制。
相同的程序,现在与JRuby一起使用。您可以看到状态为R的三个线程,这意味着它们正在并行运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| (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线程(真正的" OS级"线??程),并且没有GVL
1 2 3 4 5 6 7
| (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。由于此实现使用绿色线程,因此仅显示一个线程
1 2 3
| (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。
另外,Omniref中的C中的Ruby Threads和Global Interpreter Lock在源代码中解释了为什么Ruby线程不能并行运行。
如何使用drb?它不是真正的多线程,而是多个进程之间的通信,但是您现在可以在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(没有超线程?)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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年),下面是一些补充:
Opal是从Ruby到JavaScript的源到源编译器。它还有一个Ruby corelib的实现。它当前非常活跃,已经开发了,并且存在大量的(前端)框架。
准备生产。由于基于javascript,因此不支持并行线程。
truffleruby是Ruby编程语言的高性能实现。 TruffleRuby是Oracle Labs在GraalVM上构建的,它是JRuby的分支,将其与Rubinius项目的代码结合在一起,还包含来自Ruby,MRI的标准实现的代码,仍在现场开发中,尚未投入生产。
这个版本的ruby似乎是为性能而生的,我不知道是否支持并行线程,但我认为应该。
这是有关Rinda的一些信息,它是Linda的Ruby实现(并行处理和分布式计算范例)http://charmalloc.blogspot.com/2009/12/linda-tuples-rinda-drb-parallel.html
如果您确实需要在Ruby中为生产级系统(不能使用Beta)进行并行处理,则处理可能是一个更好的选择。
但是,绝对值得首先在JRuby下尝试线程。
另外,如果您对将来在Ruby下进行线程化感兴趣,则可能会发现本文很有用。
如果您正在使用MRI,则可以将C中的线程代码作为扩展名或使用ruby-inline gem编写。