Does ruby have the Java equivalent of synchronize keyword?

后端 未结 2 1578
别那么骄傲
别那么骄傲 2020-12-16 01:30

Does ruby have the Java equivalent of synchronize keyword? I am using 1.9.1 and I don\'t quite see an elegant way to do this.

相关标签:
2条回答
  • 2020-12-16 01:46

    It doesn't have the synchronize keyword, but you can get something very similar via the Monitor class. Here's an example from the Programming Ruby 1.8 book:

    require 'monitor'
    
    class Counter < Monitor
      attr_reader :count
      def initialize
        @count = 0
        super
      end
    
      def tick
        synchronize do
          @count += 1
        end
      end
    end
    
    c = Counter.new
    t1 = Thread.new { 100_000.times { c.tick } }
    t2 = Thread.new { 100_000.times { c.tick } }
    t1.join; t2.join
    c.count → 200000
    
    0 讨论(0)
  • 2020-12-16 01:49

    The accepted answer doesn't represent how synchronize works!

    You can just comment out synchronize do and run accepted answer's script - output will be the same: 200_000!

    So, here is an example, to show the difference between running with/without synchronize block:

    Not thread safe example:

    #! /usr/bin/env ruby
    
    require 'monitor'
    
    class Counter < Monitor
      attr_reader :count
      def initialize
        @count = 0
        super
      end
    
      def tick i
        puts "before (#{ i }): #{ @count }"
        @count += 1
        puts "after (#{ i }): #{ @count }"
      end
    end
    
    c = Counter.new
    
    3.times.map do |i|
      Thread.new do
           c.tick i
      end
    end.each(&:join)
    puts c.count
    

    In the output you will get sometihing like that:

    before (1): 0
    after (1): 1
    before (2): 0
    before (0): 0 <- !!
    after (2): 2
    after (0): 3 <- !!
    Total: 3
    

    When the thread (0) started, count was equal to 0, but after adding +1 its value was 3.

    What happens here?

    When the threads are starting they see the initial value of count. But when each of them, try to add +1, the value became different as result of the parallel computation. Without a proper synchronization, the partial state of count is unpredictable.

    Atomicity

    Now we call these operations atomic:

    #! /usr/bin/env ruby
    
    require 'monitor'
    
    class Counter < Monitor
      attr_reader :count
      def initialize
        @count = 0
        super
      end
    
      def tick i
        synchronize do
          puts "before (#{ i }): #{ @count }"
          @count += 1
          puts "after (#{ i }): #{ @count }"
        end
      end
    end
    
    c = Counter.new
    
    3.times.map do |i|
      Thread.new do
           c.tick i
      end
    end.each(&:join)
    puts c.count
    

    Output:

    before (1): 0
    after (1): 1
    before (0): 1
    after (0): 2
    before (2): 2
    after (2): 3
    Total: 3
    

    Now, by using synchronize block, we ensure the atomicity of the add operation.

    but threads still running in random order (1->0->2)

    For detailed explanation, your can continue reading this article.

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