如何从工作进程的init中正确返回?

时间:2017-10-28 20:09:33

标签: initialization elixir return-value worker

我一直在学习elixir只有几个星期了,我想用它的应用程序模块,主管和(目前只有一个)工人设置一个合适的应用程序。

通过教程,文档,Elixir论坛和stackoverflow的日子让我了解了这一点:

  • 我需要一个具有use Application
  • 的模块
  • 需要为Supervisor设置一个单独的模块,调用Supervisor.start_link/2并存储返回的pid足以用于简单的应用程序。
  • 应用程序模块有start/2函数返回{:ok, pid}(这是其中最后一次调用的返回值,Supervisor.start_link(worker_module, options)
  • 我需要一个工人模块,它定义child_spec/2init/1start_link/1
  • Supervisor.start_link/2在具有给定选项的工作人员上调用init/1然后调用start_link/1
  • init/1返回特定值

现在,我的最后一部分有问题。根据我得到的错误,我的工作人员的init函数返回“错误值”,但我无法弄清楚应该supervisor.start_link/2的返回值不是失败。

当init被定义为init(opts)

时,我已尝试过的值
  • {:ok,self()}
  • {:ok,__ MODULE __}
  • {:ok,opts}
  • Task.start_link(fn() - > function_that_actually_does_the_work()结束)
  • :正常

这是我在第{:ok, pid} = Supervisor.start_link(MyAppMain.Worker, [])行之后从记录器获得的错误消息:

** (Mix) Could not start application myapp: exited in: MyAppMain.start(:normal, [])
    ** (EXIT) an exception was raised:
        ** (MatchError) no match of right hand side value: {:error, {:bad_return, {MyAppMain.Worker, :init, {:ok, %{}}}}}
            lib/MyAppMain.ex:15: MyAppMain.start/2
            (kernel) application_master.erl:273: :application_master.start_it_old/4

那么,Supervisor.start_link/2接受的返回值是什么?

编辑:一些实际代码

申请模块:

defmodule MyAppMain do
  use Application

  def start(_type, _args) do
    {:ok, pid} = Supervisor.start_link(MyAppMain.Worker, [])
  end
end

工人模块:

defmodule MyAppMain.Worker do
  def child_spec(opts) do
    %{
       id: MyAppMain.Worker,
       start: {__MODULE__, :start_link, []},
       restart: :transient,
       type: :worker
     }
  end

  def start_link(state) do
    do_work() # returns a "fn() -> nil end", because "expected a function"
  end

  def init(opts) do
    {:ok, %{}} # Also tried putting the elements of the above list here.
  end

  defp do_work()
    #do some work

    if(prompt_restart()) do
      do_work()
    else
      fn() -> nil end
    end
  end

  defp prompt_restart() do
    # prompt the user whether to repeat the task via IO.gets, return either true or false
  end
end

1 个答案:

答案 0 :(得分:3)

问题在于您设置应用程序的方式。您将应用程序视为主管(有点像,但不像您正在使用它),并且您的工作程序的start_link部分使用错误。该应用程序实际上只是用于启动一些顶级主管,他们自己启动一些工作人员。这是一个基本的例子:

应用:

defmodule Chat do
  use Application

  def start(_type, _args) do
    Chat.Supervisor.start_link(name: Chat.Supervisor)
  end
end

主管(由应用程序启动)

defmodule Chat.Supervisor do
  def start_link(state) do
    Supervisor.start_link(__MODULE__, nil, named: __MODULE__)
  end

  def init(opts) do
    children = [
      %{
        id: Chat.RoomWorker,
        start: {Chat.RoomWorker, :start_link, []},
        restart: :transient,
        type: :worker
     }
    ]
    Supervisor.init(children, strategy: :one_for_one)
  end
end

工人(由主管发起)

defmodule Chat.RoomWorker do
  use GenServer
  def start_link() do
    GenServer.start_link(__MODULE__, nil, named: __MODULE__)
  end

  def init(opts) do
    {:ok, []}
  end
end

start_link调用成功后返回{:ok, pid}init函数在生成的进程中发生,它返回一些{:ok, state}响应,让spawner知道它已成功启动。 start_link内容在调用者进程中运行,init代码在新生成的进程中运行。

尝试重构代码,以便应用程序启动顶级主管,并且该主管正在启动一些工作进程。虽然您可以从应用程序启动工作人员并放弃主管,但最好放弃应用程序并仅使用主管,但最好还是看看所有3个如何协同工作。