2016-11-17 109 views
1

首先,我對Elixir很陌生,所以這可能是一個誤導的問題保持父進程活着

我有一個啓動兩個進程的函數。

第一過程使用的erlang:FS庫來監視文件的變化的目錄,然後將消息發送到所述第二處理。

第二個進程等待消息,在一個目錄中的文件已發生變化,當它得到的消息,則運行重新生成一個HTML報告(這是一個發票)的功能。

它看起來像這樣:

def run_report_daemon(line_item_dir) do 

    if Process.whereis(:html_daemon) == nil do 
     spawn(HTMLInvoiceDaemon, :run_daemon, [line_item_dir]) 
     |> Process.register(:html_daemon) 
    end 

    if Process.whereis(:file_watcher) == nil do 
     :fs.start_link(:file_watcher, Path.absname(line_item_dir)) 
    end 
    Process.sleep(1000) 
    run_report_daemon(line_item_dir) 
end 

這個「作品」,但讓我困擾的是「睡眠」和遞歸調用。

我的問題:是否有更好的方式來保持包含派生我活着的過程功能的過程。如果沒有遞歸調用和睡眠,它就會死亡,並將其他進程與它聯繫起來。通過遞歸調用並且不需要休眠,它會消耗大量的處理器資源,因爲它可以非常快速地循環。

的,如果塊是必要的,因爲否則會反覆產卵和註冊過程。

我想用主管的,但當時我不知道如何啓動:主管下FS的過程,甚至在這種情況下,我需要保持起動過程活着。

我懷疑我的問題是因爲在花好月圓「做事的正確方法」的根本誤解。

(注:我大概可以做整個事情沒有產卵過程這樣的,但它是一個學習的過程)

+0

正是你在哪裏發送消息'HTMLInvoiceDaemon'?你能發佈更多的代碼嗎? – Dogbert

+0

的[:FS庫(https://github.com/synrc/fs)包括函數,允許另一個進程訂閱以接收從信息:4fs過程。在HTMLInvoiceDaemon中,我訂閱了來自file_watcher的消息,然後處理這些消息。 – Veen

+0

'line_item_dir'是否是任意的,或者您在開始時是否知道這些?還有,他們中有多少人?我曾試圖回答,但後來意識到,如果在應用程序啓動時不知道輸入內容會很難得到。 –

回答

1

我可能會做這樣的事

注意,如果其中一個崩潰,主管會重新啓動,我們將在正確的狀態,其中有一個FSWatch過程和一個InvoiceDaemon過程結束,以及InvoiceDaemon過程中會聽FSWatch過程。

策略是rest_for_one如果FSWatch進程死亡,將會重新啓動InvoiceDaemon進程。這將防止InvoiceDaemon進程被訂閱到不再存在的FSWatch。如果InvoiceDaemon崩潰,那麼就沒有必要重新啓動FSWatch過程,因爲當InvoiceDaemon回來了,將重新訂閱,然後再運行下去。

defmodule Thing do 
    use Supervisor 
    # Wraps the :fs.start_link 
    # thing in a process and goes to sleep 
    defmodule FSWatch do 
    def start_link(target_dir) do 
     watcher = spawn_link(fn -> 
     IO.puts "Watching #{target_dir}" 
     :fs.start_link(:file_watcher, target_dir) 
     :timer.sleep(:infinity) 
     end) 

     {:ok, watcher} 
    end 
    end 

    # Subscribe to the file watcher and then listen 
    # forever 
    defmodule InvoiceDaemon do 
    def start_link do 
     daemon = spawn_link(fn -> 
     :fs.subscribe(:file_watcher, "/path") 
     run() 
     end) 

     {:ok, daemon} 
    end 

    defp run do 
     receive do 
     {_, {:fs, file_event}, path} -> 
      IO.puts "Got a file event #{inspect path}" 
      run() 
     e -> 
      IO.puts "unexpected event #{inspect e}" 
      run() 
     end 
    end 
    end 

    def start_link do 
    target_dir = "/foo/bar" 

    children = [ 
     worker(FSWatch, [target_dir]), 
     worker(InvoiceDaemon, []) 
    ] 
    IO.inspect self 
    Supervisor.start_link(children, strategy: :rest_for_one) 
    end 
end 

Thing.start_link 
:timer.sleep(:infinity) 
0

如果你不打算使用的主管,這是正確的方式,你可以使用一個鏈接/監視這些進程。

相反睡眠/遞歸調用,您鏈接/監視器的進程。然後你等待下來的消息,當它們中的任何一個消失時發送給你。

的好處是,你的封鎖接收,所以沒有資源消耗。

其實,這是一個監督者的基礎,因此我會建議之一,如果可能的。

更多閱讀 http://elixir-lang.org/docs/stable/elixir/Process.html

查找到spawn_link和spawn_monitor功能。

+0

的選擇我會提供你更多的例子一個目錄中選擇了這條路。我不能,因爲我現在在移動。 – vfsoraki

+0

謝謝!這基本上是我昨晚提出的解決方案:等待接收塊並捕獲退出消息,然後再進行遞歸調用。 – Veen