It depends on the implementation:
- MRI doesn't have, YARV is closer.
- JRuby and MacRuby have.
Ruby has closures as Blocks
, lambdas
and Procs
. To take full advantage of closures and multiple cores in JRuby, Java's executors come in handy; for MacRuby I like GCD's queues.
Note that, being able to create real "OS-level" threads doesn't imply that you can use multiple cpu cores for parallel processing. Look at the examples below.
This is the output of a simple Ruby program which uses 3 threads using Ruby 2.1.0:
(jalcazar@mac ~)$ ps -M 69877USER PID TT %CPU STAT PRI STIME UTIME COMMANDjalcazar 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
As you can see here, there are four OS threads, however only the one with state R
is running. This is due to a limitation in how Ruby's threads are implemented.
Same program, now with JRuby. You can see three threads with state R
, which means they are running in parallel.
(jalcazar@mac ~)$ ps -M 72286USER PID TT %CPU STAT PRI STIME UTIME COMMANDjalcazar 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
The same program, now with MacRuby. There are also three threads running in parallel. This is because MacRuby threads are POSIX threads (real "OS-level" threads) and there is no GVL
(jalcazar@mac ~)$ ps -M 38293USER PID TT %CPU STAT PRI STIME UTIME COMMANDjalcazar 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
Once again, the same program but now with the good old MRI. Due to the fact that this implementation uses green-threads, only one thread shows up
(jalcazar@mac ~)$ ps -M 70032USER PID TT %CPU STAT PRI STIME UTIME COMMANDjalcazar 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
If you are interested in Ruby multi-threading you might find my report Debugging parallel programs using fork handlers interesting.
For a more general overview of the Ruby internals Ruby Under a Microscope is a good read.
Also, Ruby Threads and the Global Interpreter Lock in C in Omniref explains in the source code why Ruby threads don't run in parallel.