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