如何从工作进程的 init 中正确 return?
How do I properly return from a worker process's init?
我学习 elixir 才几个星期,我想用它的应用程序模块、Supervisor 和(目前只有一个)worker 来设置一个合适的应用程序。
经过几天的教程、文档、Elixir 论坛和 Whosebug 让我有了这样的理解:
- 我需要一个包含
use Application
的模块
- 我不需要 Supervisor 有一个单独的模块,调用
Supervisor.start_link/2
并存储returned pid 就足够了一个简单的申请。
- Application 模块有一个
start/2
函数,它 returns {:ok, pid}
(这是其中最后一次调用的 return 值,Supervisor.start_link(worker_module, options)
)
- 我需要一个Worker模块,它定义了
child_spec/2
、init/1
和start_link/1
Supervisor.start_link/2
使用给定的选项 调用 init/1
然后 start_link/1
init/1
return某个值
现在,我对最后一部分有疑问。根据我得到的错误,我的工人的 init
函数 return 是 "bad value",但我无法弄清楚 应该 是什么 return supervisor.start_link/2
的值不会失败。
我已经尝试过的值,当init被定义为init(opts)
:
- 无
- {:ok, self()}
- {:好的,__MODULE__}
- {:好的,选择}
- 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
接受哪些 return 值?
编辑:一些实际代码
应用模块:
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
问题在于您设置应用程序的方式。您将您的应用程序视为主管(有点像,但不像您在使用它),并且您的 Worker 的 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
Worker(由 Supervisor 启动)
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
在成功时调用 return {:ok, pid}
。 init
函数发生在生成进程中,它 return 一些 {:ok, state}
响应让生成器知道已成功启动。 start_link
东西在调用者进程中运行,init
代码在新生成的进程中运行。
尝试重构您的代码,使应用程序启动顶级主管,并且该主管正在启动一些工作进程。虽然您可以从 Application 启动 worker 而放弃 Supervisor,但最好放弃 Application 并只使用 Supervisor,但最好看看这三者如何协同工作。
我学习 elixir 才几个星期,我想用它的应用程序模块、Supervisor 和(目前只有一个)worker 来设置一个合适的应用程序。
经过几天的教程、文档、Elixir 论坛和 Whosebug 让我有了这样的理解:
- 我需要一个包含
use Application
的模块
- 我不需要 Supervisor 有一个单独的模块,调用
Supervisor.start_link/2
并存储returned pid 就足够了一个简单的申请。 - Application 模块有一个
start/2
函数,它 returns{:ok, pid}
(这是其中最后一次调用的 return 值,Supervisor.start_link(worker_module, options)
) - 我需要一个Worker模块,它定义了
child_spec/2
、init/1
和start_link/1
Supervisor.start_link/2
使用给定的选项 调用 init/1
return某个值
init/1
然后 start_link/1
现在,我对最后一部分有疑问。根据我得到的错误,我的工人的 init
函数 return 是 "bad value",但我无法弄清楚 应该 是什么 return supervisor.start_link/2
的值不会失败。
我已经尝试过的值,当init被定义为init(opts)
:
- 无
- {:ok, self()}
- {:好的,__MODULE__}
- {:好的,选择}
- 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
接受哪些 return 值?
编辑:一些实际代码
应用模块:
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
问题在于您设置应用程序的方式。您将您的应用程序视为主管(有点像,但不像您在使用它),并且您的 Worker 的 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
Worker(由 Supervisor 启动)
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
在成功时调用 return {:ok, pid}
。 init
函数发生在生成进程中,它 return 一些 {:ok, state}
响应让生成器知道已成功启动。 start_link
东西在调用者进程中运行,init
代码在新生成的进程中运行。
尝试重构您的代码,使应用程序启动顶级主管,并且该主管正在启动一些工作进程。虽然您可以从 Application 启动 worker 而放弃 Supervisor,但最好放弃 Application 并只使用 Supervisor,但最好看看这三者如何协同工作。