保持父进程活着

时间:2016-11-17 12:27:40

标签: elixir

首先,我对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

这“有效”,但困扰我的是“睡眠”和递归呼叫。

我的问题:是否有更好的方法来保持包含生成我的进程的函数的进程。如果没有递归调用和睡眠,它就会死掉并带走其他进程。通过递归调用和无睡眠,它消耗了大量处理器资源,因为它可以非常快速地循环。

if块是必需的,否则会反复生成并注册进程。

我想过使用Supervisor,但后来我不确定如何在主管下启动:fs进程,即使在这种情况下我也需要保持启动进程。

我怀疑我的问题是因为对Elixir中“正确做事方式”的基本误解。

(注意:我可以在没有这样的产卵过程的情况下完成整个过程,但这是一个学习练习)

2 个答案:

答案 0 :(得分:1)

我可能会做这样的事情

请注意,如果其中任何一个崩溃,主管将重新启动它,我们将以正确的状态结束,其中有一个InvoiceDaemon进程和一个InvoiceDaemon进程,并且FSWatch进程将监听rest_for_one进程。

策略为InvoiceDaemon,如果FSWatch进程终止,它将重新启动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) 重新启动时,它将重新订阅然后永久运行。

gc()

答案 1 :(得分:0)

如果您不打算使用主管,这是正确的方法,您可以使用链接/监视器来处理这些过程。

而不是睡眠/递归调用,您链接/监视这些进程。然后等待一条关闭消息,当它们中的任何一个死亡时发送给你。

好处是你阻止接收,所以没有资源消耗。

实际上,这是主管的基础,如果可能的话我会推荐一个。

更多阅读 http://elixir-lang.org/docs/stable/elixir/Process.html

查看spawn_link和spawn_monitor函数。