I'm working on a multi-process file processing script. After trying out threads/forking, I found out about IPC (pipe/socket) and last but not least DRb. It seems like the most capable of all the options and relatively user friendly.
I was reading in about thread safety at: https://en.wikibooks.org/wiki/Ruby_Programming/Standard_Library/DRb
But when I tried their examples I don't seem to get a thread-safe result.
Thread safe server:
require 'drb'
require 'thread'
class MyStore
def initialize
@hash = { :counter=>0 }
@mutex = Mutex.new
end
def inc(elem)
@mutex.synchronize do
self[elem] = self[elem].succ
end
end
def [](elem)
@hash[elem]
end
def []=(elem,value)
@hash[elem] = value
end
end
mystore = MyStore.new
DRb.start_service('druby://localhost:9000', mystore)
DRb.thread.join
Client:
require 'drb'
obj = DRbObject.new(nil, 'druby://localhost:9000')
STDOUT.sync = true
100.times do
puts obj[:counter]
obj.inc(:counter)
obj[:lastaccess] = Time.now
end
I'm running the server code first in the background. I later launch the client code twice:
ruby client.rb > 1.txt & ; ruby client.rb > 2.txt
Now I'm expecting to see different numbers in files 1.txt and 2.txt since each client takes control of the counter and doesn't release it until it performed the increment.
What obvious issue am I missing? :)
The problem is inside your loop. The server's inc
method is thread-safe. However, your access to obj[:counter]
is not thread-safe. So you will notice that when you run your example it is doing 200 total increments (100 for each process) because you can see the last number printed is 199. That means the inc
requests are properly being queued and executed individually.
The reason you're not actually seeing all 200 numbers (0-199) individually printed (i.e. you see some duplicate numbers in the two files) is because your loop is just executing puts obj[:counter]
as soon as it hits that line of code. The current value of obj[:counter]
is being printed regardless of the mutex state because you're not checking to see if its currently locked. That means each file is printing 100 total numbers between 0-199, but they're not guaranteed to be different between the two files.
To make your example work you would want to do the printing inside of the code that is locked by the mutex, then add an extra argument to the function so you can check which client process it came from. Or in my opinion you already proved it worked because the increment happens 200 times.