Blogged by Ujihisa. Standard methods of programming and thoughts including Clojure, Vim, LLVM, Haskell, Ruby and Mathematics written by a Japanese programmer. github/ujihisa

Wednesday, February 17, 2010

Multi Process Programming With DRb

Usually Ruby's thread works well, but when you have to use thread-unsafe features like Dir.chdir, you need to use fork. How can you wait for the children processes certainly finish the given tasks? The answer is to use DRb.

I'll show an example. Prepare the following two files and run them:

server.rb

require 'drb'
x = Array.new(10, false)
def x.work(i)
  cost = rand
  puts "work(#{i}) costs #{cost}sec."
  sleep cost
  self[i] = true
end

def x.finish?
  return false unless all?
  DRb.stop_service
  true
end
DRb.start_service 'druby://localhost:1234', x

puts DRb.uri
DRb.thread.join

client.rb

require 'drb'
x = DRbObject.new nil, 'druby://localhost:1234'
10.times do |i|
  fork { x.work(i) }
end

def wait_until
  loop do
    yield and return
    sleep 0.1
  end
end

t = Time.now
wait_until { x.finish? }
puts "#{Time.now - t}sec."

run!

$ ruby server.rb &
$ ruby client.rb
...

The server has 10 small virtual tasks which can be finished within 1 second. The x.work(i) is the definition. Because of that, ideally the whole process finish maximally in 1sec, but practically because of reasons such as the definition os wait_until, the whole process will finish maximally about 1.1sec.

require 'drb'
n = 10

# Server
x = Array.new(n, false)
def x.work(i)
  cost = rand
  puts "work(#{i}) costs #{cost}sec."
  sleep cost
  self[i] = true
end

def x.finish?
  return false unless all?
  DRb.stop_service
  true
end

fork do
  DRb.start_service 'druby://localhost:1234', x
  puts DRb.uri
  DRb.thread.join
end

# Client
def wait_until
  loop do
    yield and return
    sleep 0.1
  end
end

x = DRbObject.new nil, 'druby://localhost:1234'
sleep 0.2 # FIXME

n.times do |i|
  fork { x.work(i) }
end

t = Time.now
wait_until { x.finish? }
puts "#{Time.now - t}sec."

I think you noticed that there is a dirty code in it. Yes, "sleep 0.2 # FIXME" is. I tried to find how to wait until the DRbObject certainly connected, but I couldn't find any good solution.

Without wait_until

I wrote the process at exit in the method in the server x.work in order not to use wait_until.

require 'drb'
n = 10

# Server
x = Array.new(n, false)
def x.work(i, t)
  cost = rand
  puts "work(#{i}) costs #{cost}sec."
  sleep cost
  self[i] = true

  if finish?
    puts "Total: #{Time.now - t}sec."
  end
end

def x.finish?
  return false unless all?
  DRb.stop_service
  true
end

fork do
  DRb.start_service 'druby://localhost:1234', x
  puts DRb.uri
  DRb.thread.join
end

# Client
x = DRbObject.new nil, 'druby://localhost:1234'
sleep 0.2 # FIXME

t = Time.now
n.times do |i|
  fork do
    x.work(i, t)
  end
end

At first I used block passing style, but later I found that it's impossible to pass block through druby.

Or

Use Rinda which is a higher layer library using DRb.

No comments:

Post a Comment

Followers