Ruby有Java中的synchronize关键字的等效物吗?

13

Ruby有没有Java中synchronize关键字的等价物?我正在使用1.9.1版本,但我并没有看到一种优雅的方法来实现这个。


你可以尝试一下我的Ruby gem syncem,它可以使你对象中的所有方法同步化。 - undefined
2个回答

16

虽然它没有synchronize关键字,但是您可以通过Monitor类获得非常类似的功能。这是来自《Programming Ruby 1.8》书籍的示例:

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

7
这是一条通用的编程语言设计原则:只有糟糕的编程语言才需要为每个东西添加关键字或新的语言特性。设计良好的语言可以通过简单的库完成。 - Jörg W Mittag
9
如果你不想继承自 Monitor,也没关系。只需要在你的类中包含 MonitorMixin 模块(require 'monitor' 会同时引入它),你就能免费获得相同的行为。 - Chuck
4
看起来我们找到了一个LISP程序员 ;) - jklp
我只是注释掉了synchronize do,但答案仍然是“200_000”。这里的诀窍在哪里? - skywinder
我添加了另一个答案,并解释了synchronize的工作原理。https://dev59.com/dnA75IYBdhLWcg3wlqSh#30840316 希望它有助于感受到差异。 - skywinder

14

被接受的答案并不代表同步的工作方式!

你只需将synchronize do注释掉,然后运行已接受答案的脚本 - 输出将是相同的:200_000

因此,这里有一个示例,展示使用/不使用synchronize块之间的区别:

非线程安全示例:

#! /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

在输出中,您将获得类似于以下内容:

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

当线程 (0) 启动时,count 的值为 0,但在添加 +1 后,其值变为 3

这里发生了什么?

当启动线程时,它们会看到 count 的初始值。但是每个线程尝试添加 +1 时,由于并行计算的结果,值会变得不同。没有适当的同步,count 的部分状态是不可预测的。

原子性

现在我们将这些操作称为原子操作

#! /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

输出:

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

现在,通过使用synchronize块,我们确保了添加操作的原子性

但是线程仍然以随机顺序运行(1->0->2)。

有关详细解释,请继续阅读此文章


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接