Ruby DRb - 线程安全

Ruby DRb - Thread-Safety

我正在开发一个多进程文件处理脚本。 在试用 threads/forking 之后,我发现了 IPC (pipe/socket) 和最后但并非最不重要的 DRb。 它似乎是所有选项中功能最强大的,而且相对用户友好。

我正在阅读有关线程安全的文章: https://en.wikibooks.org/wiki/Ruby_Programming/Standard_Library/DRb

但是当我尝试他们的示例时,我似乎没有得到线程安全的结果。

线程安全服务器:

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

客户:

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

我运行服务器代码在后台优先。 我稍后启动客户端代码两次:

ruby client.rb > 1.txt & ; ruby client.rb > 2.txt

现在我希望在文件 1.txt 和 2.txt 中看到不同的数字,因为每个客户端都控制了计数器并且在执行增量之前不会释放它。

我遗漏了什么明显的问题? :)

问题出在你的循环中。服务器的 inc 方法是线程安全的。但是,您对 obj[:counter] 的访问不是线程安全的。因此,您会注意到,当您 运行 您的示例时,它总共进行了 200 次增量(每个进程 100 次),因为您可以看到最后打印的数字是 199。这意味着 inc 请求已正确排队并单独执行。

你实际上没有看到所有 200 个数字 (0-199) 单独打印的原因(即你在两个文件中看到一些重复的数字)是因为你的循环只是执行 puts obj[:counter]它击中了那行代码。 obj[:counter] 的当前值无论互斥锁状态如何都会被打印,因为您没有检查它当前是否被锁定。这意味着每个文件打印 0-199 之间的 100 个总数,但不能保证两个文件之间的数字不同。

为了使您的示例正常工作,您需要在被互斥体锁定的代码内部进行打印,然后向该函数添加一个额外的参数,以便您可以检查它来自哪个客户端进程。或者在我看来你已经证明它有效,因为增量发生了 200 次。

Mike 的回答很好,但我无法在评论中添加好看的代码,因此我单独回答。

我将互斥部分更改为:

    @mutex.synchronize do
      self[elem] = self[elem].succ
      return @hash[elem]
    end

和客户端:

    num = obj.inc(:counter)
    puts num

结果文件互斥过多。

1.txt:

1
3
4
6
8
10

2.txt:

2
5
7
9
11
13

最后一个数字是 200 ,正如两个进程所期望的那样,每个进程递增 100 次。