Ruby: Is using `srand` in initializer thread safe?

159 Views Asked by At

Is the follow code thread safe? I am worried srand may be a variable that is not thread safe. I have a rails app and going to be using Puma with 5-10 threads. I do not want to cause issues.

class Test
  def initialize(seed)
    srand seed          # => 1, 222, 222, 111
  end                   # => :initialize

  def letter
    %w[a b c d e f g h i j k l m n o p q r s t u v w x y z aa bb cc dd].sample  # => "g", "u"
  end                                                                           # => :letter
  
  def letter1
    %w[a b c d e f g h i j k l m n o p q r s t u v w x y z aa bb cc dd].sample  # => "g", "u"
  end                                                                           # => :letter1
end                                                                             # => :letter1

Test.new(222).letter   # => "g"
Test.new(222).letter1  # => "g"

Test.new(111).letter   # => "u"
Test.new(111).letter1  # => "u"
1

There are 1 best solutions below

0
On

Depends what you mean by "thread-safe". The program will still work, Ruby won't enter in an inconsistent state and no data will be lost. However, the default random number generator is global, shared between threads; it will be up to thread timing to see which thread receives which random number. If your intention is that all threads just get random numbers from a single RNG, your code is fine - though the results might not be repeatable, which probably defeats the purpose of srand.

If you want to make sure that each Test only generates numbers independently (and repeatably), you want to have each Test have its own random number generator:

class Test
  def initialize(seed)
    @random = Random.new(seed)
  end

  def letter
    %w[a b c d e f g h i j k l m n o p q r s t u v w x y z aa bb cc dd].sample(random: @random)
  end
end

t1 = Test.new(111)
t2 = Test.new(222)
3.times.map { t1.letter }
# => ["u", "m", "u"]
3.times.map { t2.letter }
# => ["u", "m", "u"]