产生的Elixir进程崩溃后,不会再产生其他进程

时间:2018-07-16 22:44:45

标签: erlang elixir

在我的程序中,读取CSV文件中的每一行之后,都会产生一个新的过程来下载该图像并将其保存到文件系统:

defmodule Downloader.CLI do
  alias Downloader.Parser
  alias Downloader.Getter

  def main(_args \\ []) do
    Enum.map(Parser.run, fn(line) ->
       line -> handle_download(line)
    end)
  end

  defp handle_download({ :ok, %{ "image_id" => image_id } }) do
    pid = spawn(Getter, :run, [])
    send(pid, {self(), image_id})

    receive do
      :ok -> nil
      err -> IO.inspect(err)
    end
  end
end

如果此CSV文件包含1000张图像,则将在VM中创建1000个不同的e剂工艺。但是,如果这些进程中只有一个抛出异常,则其他任何进程都不会继续。也就是说,可执行文件不会冻结,但是不会下载其他图像。

为什么会这样?如果其他进程彼此独立,为什么它们不能继续执行?我觉得自己缺少一些简单的东西,但在其他任何地方都找不到。

1 个答案:

答案 0 :(得分:5)

问题receive do等待一条永远不会到达的消息。

这是逐步发生的事情:

  1. spawn创建流程pid
  2. 进程pid崩溃,然后将消息发送回初始进程
  3. 初始进程停留在接收功能中:它等待消息。只要不发送消息,一切都将保持不变。下载后Enum.map将不会启动。

为说明此问题,请在iex中运行以下脚本:

Process.send_after(self(), "Hi Luis", 30000)
receive do
    mess -> IO.inspect mess
end

您只会在30秒后收到"Hi Luis",在那30秒内,您的iex将等待消息,因为它是同步的。

解决方案::确保始终发送回一条消息,例如使用Process.monitor/1Supervisor

defmodule Downloader.CLI do
  alias Downloader.Parser
  alias Downloader.Getter

  def main(_args \\ []) do
    Enum.map(Parser.run, fn(line) ->
       line -> handle_download(line)
    end)
  end

  defp handle_download({ :ok, %{ "image_id" => image_id } }) do
    pid = spawn(Getter, :run, [])
    Process.monitor(pid)
    send(pid, {self(), image_id})

    receive do
      :ok -> nil
      err -> IO.inspect(err)
    end
    flush   # To avoid having a Down message ending prematurely next download 
  end
end