1

我正在研究多进程文件处理脚本。在尝试了线程/分叉之后,我发现了 IPC(管道/套接字)以及最后但并非最不重要的 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 中看到不同的数字,因为每个客户端都控制了计数器并且在执行增量之前不会释放它。

我错过了什么明显的问题?:)

4

2 回答 2

1

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

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

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

于 2015-12-14T15:47:14.063 回答
0

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 倍。

于 2015-12-14T16:49:51.397 回答