2012-03-10 46 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