2012-03-10 47 views
1

我一直在可观察对象内使用Process.fork,但发现它干扰了观察者对象文件输出的输出。Process.fork会影响Ruby中的文件io吗?

当我注释掉Process行时,输出的文件包含16行,每行的编号为0-15。但是,取消注释时,该文件包含0-15行之间的136行无序数字。是否将Process注释掉,将正确的数字打印到屏幕上。

这是部分预期的行为,还是这是一个错误?有没有人有任何想法如何解决这个问题?

下面的代码重现了问题,并通过剥离原始代码创建,直到刚好足以证明问题。使用Process.fork的最初原因是为了加快处理速度,创建了多个进程。

require 'observer' 

class Recorder 
    def initialize(notifier, filename) 
    notifier.add_observer(self) 
    @save_file = File.open(filename, 'w') 
    @i = 0 
    end 

    def update 
    puts @i 
    @save_file.puts @i 
    @i += 1 
    end 


    def stop 
    @save_file.close 
    end 
end 


class Notifier 
    include Observable 

    def run 
    16.times do 
     # When the following two Process lines are uncommented, 
     # the file output from the Recorder above is erratic 
     Process.fork {exit} 
     Process.wait 

     changed 
     notify_observers 
    end 
    end 
end 


notifier = Notifier.new 
recorder = Recorder.new(notifier, 'test.data') 

notifier.run 
recorder.stop 

回答

4

当你叉,子进程将包含父母的打开文件的克隆,与任何数据在其缓冲器中待决。当孩子退出时,它将刷新此数据并关闭其打开的文件。这不影响父代或兄弟的打开文件,但是由于它们都映射到相同的内核fd,所有数据都会转到同一个输出文件。

第一次通过fork,没有挂起的输出,所以当孩子存在时不会写任何东西。第二次,有一个“0 \ n”正在等待它退出时写入,下一次有“0 \ n1 \ n”缓冲等等。分叉的进程可能不会按创建的顺序退出(他们是异步的),因此你的混乱的结果。

叉子保留打开的文件和套接字,所以需要小心谨慎地管理它们。

您可以通过告诉ruby在每次写入时刷新输出而不是缓冲来修复此行为。

class Recorder 
    def initialize(notifier, filename) 
    notifier.add_observer(self) 
    @save_file = File.open(filename, 'w') 
    @save_file.sync = true # don't buffer this file 
    @i = 0 
    end 
end 
+0

谢谢,上面的工作。我编辑你的答案,因为同步线没有什么区别,但原理是正确的,并使用flush来代替。 – 2012-03-12 06:39:18

+0

很奇怪,IO#sync =不适合你。我尝试了@ save_file.sync = true与你的代码,它与ruby 1.9.2p180(2011-02-18修订版30909)[x86_64-linux]和ruby 1.8.7(2010-01-10 patchlevel 249)[x86_64 -linux] – dbenhur 2012-03-12 15:23:05