【问题标题】:Elixir, how running multiple processes under the supervision of a single SupervisorElixir,如何在单个 Supervisor 的监督下运行多个进程
【发布时间】:2020-11-02 02:52:27
【问题描述】:

有一个由三个模块组成的程序。 Print 模块从键盘接收一个数字,将它传递给另一个模块,接收响应,并将其显示在屏幕上。 Proc1Proc2 模块接收一个数字,执行计算,并将结果发回。

   defmodule Launch do
  @moduledoc """
  Documentation for `Launch`.
  """

  @doc """
  """
  def start() do
    children = [
      %{
        id: Print,
        start: {Print, :print, []}
      },
      %{
        id: Proc1,
        start: {Proc1, :proc1, []}
      },
      %{
        id: Proc2,
        start: {Proc2, :proc2, []}
      }
    ]

    Supervisor.start_link(children, strategy: :one_for_one)
  end
end

defmodule Print do
  def print() do
    num =
      IO.gets("Input number: ")
      |> String.trim()
      |> String.to_integer()

    if num >= 0 do
      send(Proc1, {self(), num})
    else
      send(Proc2, {self(), num})
    end

    receive do
      num -> IO.puts(num)
    after
      500 ->
        print()
    end
    print()
  end
end

defmodule Proc1 do
  def proc1() do
    receive do
      {pid, num} ->
        send(pid, 100/num)
        proc1()
      _e ->
        IO.puts("Error")
    end
  end
end

defmodule Proc2 do
  def proc2() do
    receive do
      {pid, num} ->
        send(pid, 1000/num)
        proc2()
      _e ->
        IO.puts("Error")
    end
  end
end

我正在尝试在单个主管的监督下运行所有​​进程。但是有一个问题——只有第一个“孩子”启动了,其他的“孩子”没有启动。在上面的示例中,Print 进程将启动,但 Proc1Proc2 不会启动。如何在一个主管下运行所有​​进程?重要提示:Print进程必须获取Proc1和Proc2进程的地址才能通信。

【问题讨论】:

    标签: process elixir


    【解决方案1】:

    您发布的代码存在很多问题。

    注册进程

    为了能够在调用Kernel.send/2 时将进程名称用作Process.dest(),应该启动named process

    Supervisor.start_link/2

    Supervisor.start_link/2 需要一个元组列表,其中包含立即返回的模块 函数,进程作为副作用启动。这些函数被调用,并没有什么神奇的:如果这是一个无限递归的函数,执行流程会在里面死锁,等待receive/1中的消息。

    Supervisor 通过为您自动监控重新启动子进程来执行一些魔法,但它不会产生单独的进程。 GenServer 封装了这个功能,并提供了一种方便的方法,让您不必担心生成进程。

    解决方案

    您可能会做的是生成所有三个进程,手动监视它们,然后对{:DOWN, ref, :process, pid, reason} 消息做出反应respawning 死进程。这正是 Supervisor 在幕后为儿童所做的有效工作。

    Launch
    defmodule Launch do
      def start() do
        proc1 = spawn(&Proc1.proc1/0)
        proc2 = spawn(&Proc2.proc2/0)
        print = spawn(fn -> Print.print(proc1, proc2) end)
    
        Process.monitor(proc1)
        Process.monitor(proc2)
        Process.monitor(print)
    
        receive do
          msg -> IO.inspect(msg)
        end
      end
    end
    
    Print
    defmodule Print do
      def print(pid1, pid2) do
        num =
          IO.gets("Input number: ")
          |> String.trim()
          |> String.to_integer()
    
        if num >= 0 do
          send(pid1, {self(), num})
        else
          send(pid2, {self(), num})
        end
    
        receive do
          num -> IO.puts(num)
        end
        print(pid1, pid2)
      end
    end
    

    其他两个模块都很好。


    这是iex中的样子

    iex|1 ▶ c "/tmp/test.ex"
    #⇒ [Launch, Print, Proc1, Proc2]
    iex|2 ▶ Launch.start    
    Input number: 10
    10.0
    Input number: 1000
    0.1
    Input number: a
    #⇒ {:DOWN, #Reference<0.3632020665.3980394506.95298>,
    #     :process, #PID<0.137.0>,
    #        {:badarg,
    #          [
    #            {:erlang, :binary_to_integer, ["a"], []},
    #            {Print, :print, 2, [file: '/tmp/test.ex', line: 22]}
    #        ]}}
    

    现在,不要将其打印出来,而是重新生成失败的进程,您将获得受监督的相互通信进程的裸机实现。对于all_for_one 可以通过以下方式实现的策略:

        receive do
          {:DOWN, _, _, _, _} ->
            Process.exit(print, :normal)
            Process.exit(proc1, :normal)
            Process.exit(proc2, :normal)
    
            start()
        end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-13
      • 2014-12-17
      • 2015-06-12
      • 1970-01-01
      • 2014-09-12
      • 1970-01-01
      • 2020-06-01
      • 1970-01-01
      相关资源
      最近更新 更多