Thread Safety: Class Variables in Ruby

后端 未结 3 586
悲哀的现实
悲哀的现实 2020-12-04 15:22

Performing writes/reads on class variables in Ruby is not thread safe. Performing writes/reads on instance variables appears to be thread safe. That said, is it thread safe

相关标签:
3条回答
  • 2020-12-04 15:57

    Instance variables are not thread safe (and class variables are even less thread safe)

    Example 2 and 3, both with instance variables, are equivalent, and they are NOT thread safe, like @VincentXie stated. However, here is a better example to demonstrate why they are not:

    class Foo
      def self.bar(message)
        @bar ||= message
      end
    end
    
    t1 = Thread.new do
        puts "bar is #{Foo.bar('thread1')}"
    end
    
    t2 = Thread.new do
        puts "bar is #{Foo.bar('thread2')}"
    end
    
    sleep 2
    
    t1.join
    t2.join
    
    => bar is thread1
    => bar is thread1
    

    Because the instance variable is shared amongst all of the threads, like @VincentXie stated in his comment.

    PS: Instance variables are sometimes referred to as "class instance variables", depending on the context in which they are used:

    When self is a class, they are instance variables of classes(class instance variables). When self is a object, they are instance variables of objects(instance variables). - WindorC's answer to a question about this

    0 讨论(0)
  • 2020-12-04 16:03

    Examples 2 and 3 are exactly the same. Modules and classes are also objects, and defining a singleton method on a object actually defines it on its singleton class.

    With that said, and since you have already established instance variable access is thread safe, examples 2 and 3 are thread safe. Example 1 should also be thread safe, but it is inferior to the other two because it requires manual variable synchronization.

    However, if you need to take advantage of the fact that class variables are shared within the inheritance tree, you may have to use the first approach.


    The inherent thread safety of the Ruby language depends on the implementation.

    MRI, before 1.9, implemented threads at the VM level. This means that even though Ruby is capable of scheduling code execution, nothing is really running in parallel within a single Ruby process. Ruby 1.9 uses native threads synchronized with a global interpreter lock. Only the context which holds the lock may execute code.

    n, x = 10, 0
    
    n.times do
      Thread.new do
        n.times do
          x += 1
        end
      end
    end
    
    sleep 1
    puts x
    # 100
    

    The value of x is always consistent on MRI. On JRuby, however, the picture changes. Multiple executions of the same algorithm yielded the values 76, 87, 98, 88, 94. The result could be anything because JRuby uses Java threads, which are real threads and execute in parallel.

    Just like in the Java language, manual synchronization is required in order to safely use threads in JRuby. The following code always results in consistent values for x:

    require 'thread'
    n, x, mutex = 10, 0, Mutex.new
    
    n.times do
      Thread.new do
        n.times do
          mutex.synchronize do
            x += 1
          end
        end
      end
    end
    
    sleep 1
    puts x
    # 100
    
    0 讨论(0)
  • 2020-12-04 16:21

    Examples 2 and 3 are exactly the same. They are not thread-safety at all.

    Please see the example Below.

    class Foo
      def self.bar
        @bar ||= create_no
      end
    
      def self.create_no
        no = rand(10000)
        sleep 1
        no
      end
    end
    
    10.times.map do
      Thread.new do
        puts "bar is #{Foo.bar}"
      end
    end.each(&:join)
    

    It's result is not same. The result is same when using mutex as below.

    class Foo
      @mutex = Mutex.new
    
      def self.bar
        @mutex.synchronize {
          @bar ||= create_no
        }
      end
    
      def self.create_no
        no = rand(10000)
        sleep 1
        no
      end
    end
    
    10.times.map do
      Thread.new do
        puts "bar is #{Foo.bar}"
      end
    end.each(&:join)
    

    It is run on CRuby 2.3.0.

    0 讨论(0)
提交回复
热议问题