Ruby

Multiple Threads and Processes in Ruby

Threads

At the moment (ruby version 1.8 ) one can either branch off a different thread, which will be handled by the same ruby interpreter as the parent. Which means that all variables created outside before the branching are still shared and changes made by one thread will be reflected in another thread.

From the OS point of view there is only one thread, therefore there might be issues with fairness properties of these threads.

t = Thread.new() {
# code for the child thread goes here
}

t.join # this tell the parent thread
# to block here and wait for t.

Processes

Another way of executing 2 things at once is to branch off a completely new ruby interpreter, which can be easily done with Kernel::fork. With this approach all variables are copied and therefore changes made by one process are not seen by the other.

pid = fork {
# Child process code goes here
}

# Either of these should be used, if none is called the OS
# might accumulate zombie processes:

Process.waitpid(pid) # Parent will block here
# and wait for the child
Process.waitpid(pid, Process::WNOHANG)  # Will not block
# here, if no child is there at the moment
Process.detach(pid) # You are not interested in the exit
# code of this child and it should become
# independent

The interesting thing is that fork copies not the whole state of the interpreter, but just the current Thread, so if one had 2 Threads running already and made a fork call he would have 2 processes: 1 with 2 Threads(parent) and 1 with 1 Thread(child).

External Programs

External Programs in ruby can be run in various ways:

exec
The simples case, replaces the current process with the given command


# echoes list of files
# in current directory
exec "echo *"      

# never get here

system
Executes the given command in a subshell, returns true/false depending on the exit code and directsthe output of the program to the standard streams.

system( "echo *" )

`cmd` or %x{..}
Executes the given command in a subshell and returns the output

`date`

IO.popen
Has quite a complex signature, but essentially it makes a different process, executes the command in it and makes a pipe connection to the parent, therefore the parent can listen to the output of the program and provide it with input on the spot.If one provides a ‘-‘ string as parameter, the child process will be a ruby one, similar to fork, but still with the pipe connecting the child and the parent.

IO.popen( "-" ) {|f|
$stderr.puts "#{Process.pid} is here, f is #{f}"}

One thing to watch out for is that when supplying the ‘-‘ parameter the fork is done on the OS level, which means that this does not just copy the current thread as fork, but copies the whole interpreter. Therefore if one has a few threads running in the parent, their copies will be present in the child. This can be worked around by separating out the current thread with a ruby level fork

pid = fork {
IO.popen( "-" ) {|f|
$stderr.puts "#{Process.pid} is here, f is #{f}"}
}
waitpid(pid)

Open3.popen3
Similar to IO.popen, but provides an error output stream as well.Does not support ‘-‘, therefore only truly external commands can be run (not ruby code)

require 'open3'

Open3.popen3(cmd) { |stdin, stdout, stderr| ... }

Daemon Process

To make you ruby code run as a daemon (stay alive after the parent has terminated) you should include these lines:

exit if fork            # Parent exits, child continues.
Process.setsid          # Become session leader.
exit if fork            # Zap session leader.
# After this point you are in a daemon process

The 2 exits are mandatory

Resources

http://pleac.sourceforge.net/pleac_ruby/processmanagementetc.html
http://blog.jayfields.com/2006/06/ruby-kernel-system-exec-and-x.html

Standard

One thought on “Multiple Threads and Processes in Ruby

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s