2016-11-17 113 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