Elixir 使父进程保持活动状态

Elixir 使父进程保持活动状态,elixir,Elixir,首先,我对长生不老药很陌生,所以这可能是一个误导性的问题 我有一个启动两个进程的函数 第一个进程使用erlang:fs库监视目录中的文件更改,然后向第二个进程发送消息 第二个进程等待目录中的文件已更改的消息,当它收到该消息时,运行一个函数重新生成HTML报告(这是一个发票) 看起来是这样的: def run_report_daemon(line_item_dir) do if Process.whereis(:html_daemon) == nil do spawn(HT

首先,我对长生不老药很陌生,所以这可能是一个误导性的问题

我有一个启动两个进程的函数

第一个进程使用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,但我不确定如何在Supervisor下启动:fs流程,即使在这种情况下,我也需要保持启动流程的活力

我怀疑我的问题是因为对《长生不老药》中“正确做事方式”的根本误解


(注意:我可能不需要像这样的派生过程就可以完成整个过程,但这是一个学习练习)

我可能会这样做

请注意,如果其中任何一个崩溃,主管将重新启动它,我们将以正确的状态结束,其中有一个
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)

如果您不打算使用supervisor,这是正确的方法,您可以为这些流程使用链接/监视器

您将链接/监视这些进程,而不是休眠/递归调用。然后,您等待一条停机消息,当他们中的任何一个死亡时,该消息会发送给您

好的方面是,您正在阻止接收,因此没有资源消耗

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


有关更多信息,请参阅上的文档和函数。

您将消息发送到
HTMLInvoiceDaemon
的确切位置?您可以发布更多的代码吗?包含一个函数,允许另一个进程订阅以接收来自:fs进程的消息。在HTMLInvoiceDaemon中,我订阅来自:file_watcher的消息,然后对这些消息执行操作。
line_item_dir
是任意的还是您在开始时就知道这些?还有,他们有多少人?我曾试图回答,但后来意识到,如果应用程序启动时不知道输入,则很难获取输入。已知输入-从命令行作为选项传入的单个目录..我将提供更多您选择这种方式的示例。我不能,因为我现在在手机上。谢谢!这就是我昨晚提出的基本解决方案:在接收块等待并捕获退出消息,然后再进行递归调用。